Formal power series: Difference between revisions

Content added Content deleted
m (→‎{{header|Racket}}: s->z in Lazy)
(Added Perl Implementation)
Line 1,093: Line 1,093:
<lang parigp>sin('x)
<lang parigp>sin('x)
cos('x)</lang>
cos('x)</lang>

=={{header|Perl}}==
Although true Lazy Lists *can* be implemented using perl (using the builtin "tie" function), I felt that doing so would make the example harder to understand.

Instead, I chose to implement delayed evaluation with a generator function and a cache.

Creating a new arithmetic type in perl is relatively easy, using the "overload" module which comes with perl.

This was partly inspired by the perl6 example, but is far from being a direct translation.

<lang perl>
package FPS;
use strict;
use warnings;
use Math::BigRat;

sub new {
my $class = shift;
return bless {@_}, $class unless @_ == 1;
my $arg = shift;
return bless { more => $arg }, $class if 'CODE' eq ref $arg;
return bless { coeff => $arg }, $class if 'ARRAY' eq ref $arg;
bless { coeff => [$arg] }, $class;
}

sub coeff {
my ($self, $i) = @_;
my $cache = ($self->{coeff} ||= []);
my $more = $self->{more};
for my $j ( @$cache .. $i ) {
last unless $more;
$cache->[$j] = $more->($j, $self);
}
$cache->[$i] or 0;
}

sub invert {
my $orig = shift;
ref($orig)->new( sub {
my ($i, $self) = @_;
unless( $i ) {
my $a0 = $orig->coeff(0);
die "Cannot invert power series with zero constant term."
unless $a0;
(Math::BigRat->new(1) / $a0);
} else {
my $sum = 0;
my $terms = $self->{coeff};
for my $j (1 .. $i) {
$sum += $orig->coeff($j) * $terms->[$i - $j];
}
-$terms->[0] * $sum;
}
} );
}

sub fixargs {
my ($x, $y, $swap) = @_;
my $class = ref $x;
$y = $class->new($y) unless UNIVERSAL::isa($y, $class);
($x, $y) = ($y, $x) if $swap;
($class, $x, $y);
}

use overload '+' => sub {
my ($class, $x, $y) = &fixargs;
$class->new( sub { $x->coeff($_[0]) + $y->coeff($_[0]) } );
}, '-' => sub {
my ($class, $x, $y) = &fixargs;
$class->new( sub { $x->coeff($_[0]) - $y->coeff($_[0]) } );
}, '*' => sub {
my ($class, $x, $y) = &fixargs;
$class->new( sub {
my $i = shift;
my $sum = 0;
$sum += $x->coeff($_) * $y->coeff($i-$_) for 0..$i;
$sum;
} );
}, '/' => sub {
my ($class, $x, $y) = &fixargs;
$x * $y->invert;
}, '""' => sub {
my $self = shift;
my $str = $self->coeff(0);
for my $i (1..10) {
my $c = $self->coeff($i);
next unless $c;
$str .= ($c < 0) ? (" - " . (-$c)) : (" + ".$c);
$str .= "x^$i";
}
$str;
};

sub differentiate {
my $orig = shift;
ref($orig)->new( sub {
my $i = shift;
($i+1) * $orig->coeff($i);
} );
}

sub integrate {
my $orig = shift;
ref($orig)->new( coeff => [0], more => sub {
my $i = shift;
$orig->coeff($i-1) / Math::BigRat->new($i);
} );
}

my $sin = __PACKAGE__->new;
my $cos = 1 - $sin->integrate;
%$sin = %{$cos->integrate};
my $tan = $sin / $cos;
my $exp = __PACKAGE__->new();
%$exp = (%{$exp->integrate}, coeff => [1]);

print "sin(x) ~= $sin\n";
print "cos(x) ~= $cos\n";
print "tan(x) ~= $tan\n";
print "exp(x) ~= $exp\n";

print "sin^2 + cos^s = ", $sin*$sin + $cos*$cos, "\n";

1;
__END__
</lang>
{{out}}
<pre>
sin(x) ~= 0 + 1x^1 - 1/6x^3 + 1/120x^5 - 1/5040x^7 + 1/362880x^9
cos(x) ~= 1 - 1/2x^2 + 1/24x^4 - 1/720x^6 + 1/40320x^8 - 1/3628800x^10
tan(x) ~= 0 + 1x^1 + 1/3x^3 + 2/15x^5 + 17/315x^7 + 62/2835x^9
exp(x) ~= 1 + 1x^1 + 1/2x^2 + 1/6x^3 + 1/24x^4 + 1/120x^5 + 1/720x^6 + 1/5040x^7 + 1/40320x^8 + 1/362880x^9 + 1/3628800x^10
sin^2 + cos^s = 1
</pre>


=={{header|Perl 6}}==
=={{header|Perl 6}}==