# Perl basics

# types: scalars, vectors (lists), assoc lists (dictionary,hashmaps),
# functions, pointers to all these things (files, other aspects of
# perl not discussed).

# Scalars can be numerical values as well as strings.

$x = 1;   # don't put up with any language that don't allow a global x

$s = " abc\n";   # The $ in front is always required: indicates a sclar

print $x,$s;

# vectors
@A = (2,3,5,7,11);
foreach my $i (@A)
{
    print $i," "  # last statement in {} block don't need ;
}
print "\n";

for(my $i=0;$i<=$#A;$i++) { print $A[$i], " " }  # traditional for loop
print "\n";    

# named function
sub println {
    print(@_, "\n");
}


my ($car,@cdr) = @A;  # deconstructs list to head and tail
println $car;
println join(" ",@cdr);  # join adds spaces between values in @cdr


# static and dynamic scoping

local $pi = 3.1415927;   # change my to local
sub area
{ my $radius = $_[0]; $radius*$radius*$pi; }

{
  local $pi = 0;  # is this like 'let'?
  println("Area 51 is ", area(51));  # which $pi will it use?
}
{
  local $pi = -1;
  println("Area 51 is ", area(51));  # which $pi will it use?
}
println $pi;  # local vars are NOT GLOBALS, not exactly.


################################## for 9/21 class

my $x = 1;
local $y = 10;
sub f { $x+$y }
sub main2 {
    my $x = 2;  # statically scoped
    local $y = 20;  # dynamically scoped
    print f(), "\n";  # what will this print?
}
main2();
die;

# pointers

$px = \$x;   # equivalent to int *px=&x; in C
println("\$px is ", $px);
println("\$\$px is ",$$px);

# The usage of Perl is EXPLICIT like in C, and not IMPLICIT like in Python
# or Java.  Explicit means you have to use a special syntax to indicate
# the presence of pointers.  Implicit means pointers are being used, but
# it's not obvious from the syntax.

# So where should you use pointers in Perl?
# Answer: basically the same places they're used in Python, Java and other
# langauges, but you have to use the pointer syntax.  referencing and
# dereferencing are not automatic like in those other languages.

# Example, how do we have a nested list?

@B = ((1,2),(3,4));  # want this to be like [[1,2],[3,4]] in python

println join(" ",@B);
println @B[1];  # what do you expect? it should be (3,4) but it's 2
# The ()s alone do not indicate the existence of nested list.  But that's
# because a nested list is a list of POINTERS to other lists:  You have
# to build the intended list as follows in Perl:
@c1 = (1,2);
@c2 = (3,4);
my @C = (\@c1, \@c2);
println $C[1][0];  # prints 3, now you can treat it like a 2D array
println @{$C[1]};  # sometimes you have to use {} when dereferencing.

## pointers to functions (nameless lambdas)

$f = sub { $_[0]*2 };
println &$f(4);
println $f->(5);  # in C++ n->x is really (*n).x

$I = sub {$_[0]};
$K = sub { my $x=$_[0]; sub { $x }};
# must use my to declare vars to be consistent with lambda calc.

println $K->(1)->(2);   # (K 1 2), Curried application
println $K->($I)->(1)->(2);    

######


sub makeaccumulator {
    my $y = 0;  # change my to local and see what happens
    sub { $y = $y+$_[0]; $y }
}
my $g1 = makeaccumulator();
my $g2 = makeaccumulator();

println $g1->(2);
println $g1->(2);
println $g1->(2);
println $g2->(2);
println $g1->(2);
die;
# each of g1, g2 carries its own local state.  CLOSURE

sub newaccount {
    my ($name,$balance,$password) = @_;
    my $deposit = sub { $balance += $_[0] };
    my $withdraw = sub {
	if ($password eq $_[1]) {$balance -= $_[0]}
	else {print("motherhacker! \n")}
    };  
    my $inquiry = sub {
	if ($password eq $_[0])
	{ print("$name has a balance of $balance \n"); }
    };

    # But still need INTERFACE FUNCTION!
    my $public = sub {
	my $request = $_[0];
	if ($request eq "withdraw") {return $withdraw}
	if ($request eq "deposit") {return $deposit}
	if ($request eq "inquiry") {return $inquiry}
	print("ERROR\n");
    };
    return $public;
}#newaccount

my $hisaccount = newaccount("P. Artyan Imal", 1000,"xyzzy");
my $heraccount = newaccount("Farma N. Imal", 5000, "abcd");

$hisaccount->("withdraw")->(200,"xyzzy");
$hisaccount->("inquiry")->("xyzzy");
# lexical scoping is crucial to closure as objects concept.

sub team {
    my ($wins,$losses) = (0,0);
    my $win = sub { $wins++; };
    my $lose = sub {$losses++; };
    my $percentage = sub {
	my $games = $wins+$losses;
	if ($games==0) {return 0}
	else {return $wins*1.0/$games; }
    };
    my $compare = sub {
	$otherteam = $_[0];
	return $percentage->() - $otherteam->{"percentage"}->();
    };
    my $interface;  # use a hashmap instead of func for interface
    $interface->{"win"} = $win;
    $interface->{"lose"} = $lose;
    $interface->{"percentage"} = $percentage;
    $interface->{"cmp"} = $compare;
    return $interface;
}
my $mets = team();
my $yankees = team();
$mets->{"lose"}->();
$yankees->{"win"}->();
println $mets->{"cmp"}->($yankees);
