as some of you know, i’m a very enthousiastic haskell beginner and happy daily perl programmer. What i miss from haskell is:
- lazy arrays
- some prelude functions like takeWhile, while and filter (a lazy version of grep)
tonight, as i have to struggle one more time with very large CSV file, i realized how the while loop has become boring for me. I wrote a little haskell code.
positives = [0..]
is_even x = x `mod` 2 == 0
evens = filter is_even
main = mapM_ print
[ ("10 positives", take 10 positives)
, ("10 evens" , take 10 (evens positives) )
, ("evens < 10" , takeWhile (< 10) (evens positives) )
]
and tried to implement all my missings in perl.
* lazyness behavior can be implemented with closures
* so the subs take, takeWhile and filter use closures
* at the end of lazyness chain, i need a collect sub to get a real array
I started to code and it was quiet easy:
#! /usr/bin/perl
use 5.10.0;
use utf8;
use strict;
use warnings;
sub to_infinity_from {
my ($start) = @_;
sub { $start++ }
}
sub take {
my ( $want_more, $some ) = @_;
$want_more++;
sub { $want_more-- ? $some->() : undef }
}
sub takeWhile (&$) {
my ( $test, $list ) = @_;
sub {
local $_ = $list->();
defined or return undef;
$test->() ? $_ : undef;
}
}
sub filter (&$) {
my ( $test, $list ) = @_;
sub {
while ( defined ( local $_ = $list->() ) ) {
$test->() and return $_
}
undef;
}
}
sub collect ($) {
my ( $list ) = @_;
my @r;
while ( defined ( my $element = $list->() ) ) {
push @r,$element
}
@r;
}
for my $sequence
( [ "ten positives" => take 10, to_infinity_from(0) ]
, [ "ten evens" => take 10, filter { not( $_ % 2 ) } to_infinity_from(0) ]
, [ "evens less than 10" => takeWhile { $_ < 10 } filter { not( $_ % 2 ) } to_infinity_from(0) ]
) {
my ( $desc, $iterator ) = @$sequence;
say $desc;
while ( defined ( my $_ = $iterator->() ) ) { say $_ }
}
say $_ for "collector", collect take 10, to_infinity_from(0);
It worked! sooo easily! Now, a real live use case: I want a YAML::Dump of the first 10 adults of my csv file:
use Text::CSV;
use YAML;
use Lazyness ':all'; # collect, filter, take, takeWhile
( my $csv = Text::CSV->new({qw/ binary /})
or die Text::CSV->error_diag
)->column_names(qw/firstname lastname birthdate login /);
open my $fh,'users.csv' or die "$!";
say YAML::Dump [
collect take 10, filter {
$$_{birthdate} ~~ /(?\d{4})-/
and $+{year} < 1992
} sub { $csv->getline_hr($fh) }
];