Perl Weekly Challenge 29: Brace Expansion and Calling C Code

These are some answers to the Week 29 of the Perl Weekly Challenge organized by Mohammad S. Anwar.

Challenge # 1: Brace Expansion

Write a script to demonstrate brace expansion. For example, script would take command line argument Perl {Daily,Weekly,Monthly,Yearly} Challenge and should expand it and print like below:

Perl Daily Challenge
Perl Weekly Challenge
Perl Monthly Challenge
Perl Yearly Challenge

The specification is not very detailed, and we will not attempt to provide a full-fledged templating system, as this already exists. So we will limit our implementation to the following: an initial sentence fragment, followed by a single list of options between curly brackets, followed by a final sentence fragment.

Brace Expansion In Perl 5

We will supply a command line argument in the form of a string between quote marks, and also provide for a default value for the purpose of testing. The program also attempts to normalize spaces in the output, since it is difficult to predict the exact format (number of spaces) supplied by the user.

#!/usr/bin/perl
use strict;
use warnings;
use feature qw/say/;

my $in_str = shift // "Perl {Daily,Weekly,Monthly,Yearly} Challenge";
my ($start, $options, $end) = $in_str =~ /([^{]+) \{ ([^}]+) \} (.+)/x;
s/^ +| +$//g for ($start, $options, $end); # removing leading or trailing spaces
say "$start $_ $end" for split / *, */, $options;

Running the program using the default value and with a poorly formatted input string displays the following result:

$ perl brace-expansion.pl
Perl Daily Challenge
Perl Weekly Challenge
Perl Monthly Challenge
Perl Yearly Challenge

$ perl brace-expansion.pl "Perl {Daily,  Weekly  ,  Monthly,Yearly   }   Challenge"
Perl Daily Challenge
Perl Weekly Challenge
Perl Monthly Challenge
Perl Yearly Challenge

Brace Expansion In Perl 6

Simply porting the same P5 program to Perl 6 is straight forward:

use v6;

sub MAIN (Str $input = 'Perl {Daily,Weekly,Monthly,Yearly} Challenge') {
    my $match = $input ~~ /(<-[{]>+) '{' (<-[}]>+) '}' (.+)/;
    my ($start, $options, $end) = map { ~$_ }, $match[0 .. 2];
    s:g/^ \h+ | \h+ $// for $start, $options, $end;
    say "$start $_ $end" for $options.split(/\s*','\s*/);
}

Running the program using the default value and with a poorly formatted input string displays similar result:

$ perl6 brace-expansion.p6
Perl Daily Challenge
Perl Weekly Challenge
Perl Monthly Challenge
Perl Yearly Challenge

$ ./perl6 brace-expansion.p6 "Perl {Daily,  Weekly  ,  Monthly,Yearly   }   Challenge"
Perl Daily Challenge
Perl Weekly Challenge
Perl Monthly Challenge
Perl Yearly Challenge

Calling C Code from Perl

Write a script to demonstrate calling a C function. It could be any user defined or standard C function.

I had some environment problems and was unable to call a C library from Perl 5. I have done it in the past at work on a Linux environment without too much problem, as far as I can remember, but at home, I'm using Cygwin, and it appears to be a bit more complicated and I don't have much time to work on it

So I'll do the task only in Perl 6.

Using Native Calls in Perl 6

Starting With a Very Simple C Function

I started with a very simple C calc.c program providing an add function:

#include <stdio.h>
int add (int a, int b) {
    return a + b;
}

and a simple Perl 6 script calling it:

use NativeCall;

sub add(int32, int32)
    returns int32
    is native('./calc.dll')
    { * }

say add(3, 4);

It took me a number of faulty tries before I was able to create a shared library, and run the program:

$ gcc -c -fPIC calc.c -o calc.o

$ gcc -shared calc.o -o calc.dll

$ perl6 calc.p6
7

Benchmarking a Pure Perl 6 Program and a Native C Library

Now that we know how to run a basic function from a C library, we can try something more interesting: benchmarking a pure Perl 6 subroutine against a native C function. For this, I chose to use a recursive implementation of the Fibonacci sequence, since execution times get very long even for moderately large input. Of course, it is possible to memoize the recursive Fibonacci subroutine to obtain very small execution times, but I don't want to do it here, since I want to compare naïve recursive implementations to compare their duration.

The fibonacci.c program provides a fib function:

#include <stdio.h>
int fib (int a) {
    if (a == 0 || a == 1) {
        return 1;
    } else {
        return fib(a -1 ) + fib(a - 2);
    }
}

The fibo.p6 program below uses both the native fib function and a pure Perl 6 fib-p6 subroutine and record their execution times:

use v6;
use NativeCall;

sub fib(int32)
    returns int32
    is native('./fibonacci.dll')
    { * }

sub fib-p6 (Int $num) {
    return 1 if $num == 0 or $num == 1;
    return fib-p6($num - 1) + fib-p6($num - 2);
}

sub MAIN (Int $num where * >= 0 = 36 ) {
    my $start-time = INIT now;
    say "C library function: ", fib($num);
    say "Duration C function: ", now - $start-time;
    my $now = now;
    say "P6 subroutine: ", fib-p6 $num;
    say "Duration P6 subroutine: ", now - $now;
}

Compiling the C program, building the shared library and running the benchmark shows the following result:

$ gcc -c -fPIC fibonacci.c -o fibonacci.o

$ gcc -shared fibonacci.o -o fibonacci.dll

$ perl6 fibo.p6
C library function: 24157817
Duration C function: 0.1306511
P6 subroutine: 24157817
Duration P6 subroutine: 37.425447

The result is impressive: 0.13 seconds for the C fib function and 37 seconds for fib-p6 the pure Perl 6 implementation. With the default 36 input value, the C function runs 286 times faster!

Wrapping up

The next week Perl Weekly Challenge is due to start soon. If you want to participate in this challenge, please check https://perlweeklychallenge.org/ and make sure you answer the challenge before 23:59 BST (British summer time) on Sunday, October, 20. And, please, also spread the word about the Perl Weekly Challenge if you can.

Leave a comment

About laurent_r

user-pic I am the author of the "Think Perl 6" book (O'Reilly, 2017) and I blog about Perl (5 and 6).