perl6 functions similar to "car cdr cons" in scheme?
Asked Answered
S

2

5

I really love perl6 and scheme. I am wondering if there are functions in perl6 that are similar to the "cons, car, cdr" functions in scheme? What I have been doing feels cumbersome:

sub cons($a, $aList) { return flat($a, $aList); } # sometimes flat is undesired;
sub car($aList) { return first($aList); }
sub cdr($aList) { return tail($aList.elems - 1); }

Thanks.

Sidoney answered 11/9, 2016 at 17:24 Comment(0)
S
9

Perl 6 lists/arrays are not linked list.
However, the Pair type can be used to build linked lists, and provides the functionality of those three Lisp functions.

Using nested Pairs

A Pair represents a key-value pair. For example when iterating a Hash, you get a sequence of Pairs.

If you think of a Pair as a cons cell, then you can build a linked lists à la Lisp as a Pair that has another Pair as its value, which in turn has another Pair as its value, and so on.

  • The => operator (Pair constructor) fulfills the role of cons.
  • The .key method fulfills the role of car.
  • The .value method fulfills the role of cdr.

Example:

my $l = (1 => (2 => (3 => (4 => (5 => Nil)))));
say $l.key;    # 1
say $l.value;  # 2 => 3 => 4 => 5 => Nil

The => operator is right-associative, so that first line can also be written without the parentheses:

my $l = 1 => 2 => 3 => 4 => 5 => Nil;

If you wanted to declare the Lisp functions under their familiar names, it would look like this:

sub cons ($x, $y)  { $x => $y }
sub car  (Pair $y) { $y.key   }
sub cdr  (Pair $y) { $y.value }

Note, however, that there are no built-in convenience functions for doing list processing with such Pair-based linked lists. So if you wanted to do the equivalent of Scheme's length or append functions etc., then you'd have to write those functions yourself. All the built-in list processing routines assume normal Perl 6 lists or compatible Iterable types, which Pair is not.

Using normal Perl 6 lists

If you want to use normal Perl 6 lists/arrays as your data structure, but implement the behavior of the Lisp functions for them, I'd write it like this:

sub cons ($x, $y) { $x, |$y  }
sub car  (@y)     { @y[0]    }
sub cdr  (@y)     { @y[1..*] }

Some comments:

  • Instead of using flat, I used the | operator to slip the elements of $y into the outer list.
  • The first function does not do what you expect in your code. It is meant for searching a list. It interprets the first argument (in your case $aList) as a predicate, and the remaining arguments (in your case none) as the list to search, thus in your case it always returns Nil.
    For returning the first element of a list, You can use the [ ] positional subscripting operator instead, like I did here.
  • The return keyword is optional; The result of the last statement of a function is automatically returned.
Shoring answered 11/9, 2016 at 17:55 Comment(2)
"The last statement of a function is automatically returned." How do you return a statement? Do you mean the result of the last expression?Dagny
@KeithThompson: The last statement is treated as an expression, and its value returned. The exception is that when the last statement is a block construct (like a for loop), Nil is returned if the end of the function is reached.Shoring
F
1

Here's another set of functions, similar to your originals, and using prefix operators for car / cdr:

sub prefix:<car>(List $l) is tighter(&infix:<xx>) { $l.head }
sub prefix:<cdr>(List $l) is tighter(&infix:<xx>) { $l.tail(*-1).list }
sub cons($item, List $l) { ($item, |$l) }

These provide an almost lisp-ish syntax, if you wish.

my @l = (1, 2, 3, 4);
my $l = |@l;

say "Array";
say "car: ", (car @l).perl;
say "cdr: ", (cdr @l).perl;

say "List";
say "car: ", (car $l).perl;
say "cdr: ", (cdr $l).perl;

say "Literal";
say "car: ", (car (4, 3, 2, 1)).perl;
say "cdr: ", (cdr (4, 3, 2, 1)).perl;

say "cons: ", (cons car $l, cdr $l).perl;
Fourier answered 28/5, 2017 at 21:59 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.