tags:

views:

451

answers:

6

Are there ruby equivalents to the lisp car, cdr, and cons functions? For those unfamiliar with lisp, here's what I want from ruby:

[1,2,3].car   => 1
[1,2,3].cdr   => [2,3]
[2,3].cons(1) => [1,2,3]

(in lisp):

(car '(1 2 3))  => 1
(cdr '(1 2 3))  => (2 3)
(cons 1 '(2 3)) => (1 2 3)
+2  A: 

I'd recommend reading the Ruby API for Array. There are many methods and operators there that can do exactly what you need.

http://www.ruby-doc.org/core/classes/Array.html

Marc W
+13  A: 

Ruby arrays are not implemented as singly-linked lists, so it is not as useful to have car and cdr and stuff.

If you really wanted, you could do

[1,2,3][0]      => 1
[1,2,3].first   => 1
[1,2,3][1..-1]  => [2,3]
[1] + [2,3]     => [1,2,3]
newacct
Not only is it not useful, it isn't really meaningful. Car, cdr and cons are intrinsically linked to cons cells. Since cons cells are not involved in Ruby arrays at all, any such method would be a misnomer.
Chuck
+2  A: 
>> [1,2,3].drop 1
=> [2, 3]
>> [1,2,3].first
=> 1

Of course, as you know, these aren't too close to Lisp. The real ruby equivalent would be something like [1, [2, [3, nil]]]. You could always write a List class...or find one somewhere.

Chapter 8 of Practical Ruby Projects is called Implementing Lisp in Ruby.

DigitalRoss
+7  A: 

This is how you'd implement lisp-like single-linked lists in ruby:

class Object
  def list?
    false
  end
end

class LispNilClass
  include Enumerable
  def each
  end

  def inspect
    "lnil"
  end

  def cons(car)
    Cell.new(car, self)
  end

  def list?
    true
  end
end

LispNil = LispNilClass.new

class LispNilClass
  private :initialize
end

class Cell
  include Enumerable

  attr_accessor :car, :cdr

  def initialize(car, cdr)
    @car = car
    @cdr = cdr
  end

  def self.list(*elements)
    if elements.empty?
      LispNil
    else
      first, *rest = elements
      Cell.new(first, list(*rest))
    end
  end

  def cons(new_car)
    Cell.new(new_car, self)
  end

  def list?
    cdr.list?
  end

  # Do not use this (or any Enumerable methods) on Cells that aren't lists
  def each
    yield car
    cdr.each {|e| yield e}
  end

  def inspect
    if list?
      "(#{ to_a.join(", ") })"
    else
      "(#{car} . #{cdr})"
    end
  end
end

list = Cell.list(1, 2, 3) #=> (1, 2, 3)
list.list? #=> true
list.car #=> 1
list.cdr #=> (2, 3)
list.cdr.cdr.cdr #=> lnil
list.cons(4) #=> (4, 1, 2, 3)

notlist = Cell.new(1,2) #=> (1 . 2)
notlist.list? #=> false
notlist.car #=> 1
notlist.cdr #=> 2
notlist.cons(3) #=> (3 . (1 . 2))
sepp2k
Nice...........
DigitalRoss
+1  A: 

Semi-seriously, if you want CONS, CAR, and CDR in Ruby, you could do worse than

def cons(x,y)
   return lambda {|m| m.call(x,y)}
end

def car(z)
  z.call(lambda {|p,q| p})
end

def cdr(z)
  z.call(lambda {|p,q| q})
end

And then you can define your list procedures,

def interval(low, high)
  if (low > high)
    return nil
  else
    return cons(low, interval(low + 1, high))
  end
end

def map(f, l)
  if (l == nil)
    return nil
  else
    cons(f.call(car(l)), map(f, cdr(l)))
  end
end

def filter(p, l)
  if (l == nil)
    return nil
  elsif (p.call(car(l)))
    return cons(car(l), filter(p, cdr(l)))
  else
    return filter(p, cdr(l))
  end
end

def reduce(f, f0, l)
  if (l == nil)
    return f0
  else
    return f.call(car(l), reduce(f, f0, cdr(l)))
  end
end

And then you might get the sum of the odd squares in the range 1 to 10:

reduce(lambda {|x, y| x + y},
       0,
       filter(lambda {|x| x % 2 == 1},
              map(lambda {|x| x * x},
                  interval(1, 10))))
=> 165
Cirno de Bergerac
A: 

No there isn't, but it is easy enough to write your own if needed.

Dev er dev