Perlでも普通のリスト操作を

30分プログラム、その372。Perlの話をするたびに、id:Gemmaさんが「Perlのリストはキモい、キモすぎる。ネストするだけでリファレンスを使うなんでありえない。」と言いつづけてる。
なので、Perlでもごく普通の単方向リストを作ってみた。これなら、あたりまえにネストできるぜ。

ネタのつもりで作ったんだけど、割りといいかもしれない。特にmapとかfold_rightみたいな再帰関数が、わりとすんなり実装できるのがうれしい。

使い方

# consしてリストを作る
my $xs = List->cons(1,
          List->cons(2,
            List->cons(3,List->nil)));

# 文字列にして表示
#   => (1, 2, 3)
print $xs->to_string,"\n";

# 別のリストの作り方
my $ys = List->new(1,2,3);
print $ys->to_string,"\n";

# mapを使って、全部1づつインクリメント
#   => (2, 3, 4)
my $zs = $ys->map(sub { $_[0] + 1 });

# fold_rightを使って、合計を求める
my $sum = $ys->fold_right(sub { $_[0] + $_[1] },0);

ソースコード

#! /usr/bin/perl
# -*- mode:perl; coding:utf-8 -*-
#
# list.pl -
#
# Copyright(C) 2008 by mzp
# Author: MIZUNO Hiroki / mzpppp at gmail dot com
# http://howdyworld.org
#
# Timestamp: 2008/09/22 23:34:40
#
# This program is free software; you can redistribute it and/or
# modify it under MIT Lincence.
#
use strict;
use warnings;


package List;
use Data::Dumper;
sub new{
    my ($class,$x,@xs) = @_;
    if(@xs == 0){
	List->cons($x,List->nil);
    }else{
	List->cons($x,List->new(@xs));
    }
}

sub cons{
    my ($class,$head,$tail) = @_;
    bless({head=>$head,tail=>$tail},$class);
}

my $nil = bless({head=>undef,tail=>undef});
sub nil{
    $nil;
}

# method
sub head{
    my ($self) = @_;
    $self->{'head'};
}

sub tail{
    my ($self) = @_;
    $self->{'tail'};
}

sub is_empty{
    my ($self) = @_;
    $self == $nil;
}

sub map{
    my ($self,$f) = @_;
    if($self->is_empty){
	$self;
    }else{
	List->cons($f->($self->head),
		   $self->tail->map($f));
    }
}

sub fold_right{
    my ($self,$f,$init) = @_;
    if($self->tail->is_empty){
	$f->($self->head,$init);
    }else{
	$f->($self->head,$self->tail->fold_right($f,$init));
    }
}

sub to_string{
    my ($self) = @_;
    if($self->is_empty){
	'nil'
    }else{
	my $s =$self->fold_right(
	    sub { 
		my ($a,$b) = @_;
		if($b eq ''){ $a }else{ $a .', ' . $b} 
	    } , '');
	"(${s})";
   }
}


package main;
use Data::Dumper;

my $xs = List->cons(1,
		    List->cons(2,
			       List->cons(3,List->nil)));
print $xs->to_string,"\n";

my $ys = List->new(1,2,3);
print $ys->to_string,"\n";

print $ys->map(sub { $_[0] + 1 })->to_string,"\n";

print $ys->fold_right(sub { $_[0] + $_[1] },0),"\n";