2009-10-05 3 views
20

Esistono rubini equivalenti alle funzioni lisp car, cdr e cons? Per chi non conosce Lisp, ecco quello che voglio da rubino:Esistono rubini equivalenti a auto, cdr e contro?

[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) 

risposta

28

array di Ruby non sono implementate come liste singolarmente-legati, in modo che non è così utile avere auto e cdr e roba.

Se si voleva davvero, si potrebbe fare

[1,2,3][0]  => 1 
[1,2,3].first => 1 
[1,2,3][1..-1] => [2,3] 
[1] + [2,3]  => [1,2,3] 
+8

Non solo non è utile, non è davvero significativo. Auto, cdr e contro sono intrinsecamente legati alle celle contro. Dal momento che le cellule cons non sono affatto coinvolte negli array di Ruby, qualsiasi metodo di questo tipo sarebbe un termine improprio. – Chuck

2

Mi raccomando di leggere l'API di Ruby per Array. Ci sono molti metodi e operatori che possono fare esattamente ciò di cui hai bisogno.

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

+0

Mentre è possibile ottenere 'cdr' per un array in vari modi, incluso' ary [1 ..-- 1] ', non è proprio lo stesso IMHO che ha un metodo che fa esattamente ciò che è necessario. – Phrogz

+0

Ecco perché Ruby ha questa meravigliosa possibilità di definire metodi che fanno esattamente ciò di cui hai bisogno: 'class Array; def cdr; auto [1 ..- 1]; fine; fine' – SFEley

4
>> [1,2,3].drop 1 
=> [2, 3] 
>> [1,2,3].first 
=> 1 

Naturalmente, come sapete, questi non sono troppo vicino a Lisp. Il vero equivalente rubino sarebbe qualcosa come [1, [2, [3, nil]]]. Potresti sempre scrivere una classe List ... o trovarne una da qualche parte.

Il capitolo 8 di Practical Ruby Projects si chiama Implementazione di Lisp in Ruby.

10

In questo modo si sarebbe implementare lisp-come le liste singole-linked 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)) 
+0

Bello ........... – DigitalRoss

7

semi-serio, se si vuole CONS, auto, e CDR in Ruby, si potrebbe fare di peggio che

 
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 

E poi si può definire le procedure della lista,

 
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 

E allora si potrebbe ottenere la somma dei quadrati dispari uares nell'intervallo da 1 a 10:

 
reduce(lambda {|x, y| x + y}, 
     0, 
     filter(lambda {|x| x % 2 == 1}, 
       map(lambda {|x| x * x}, 
        interval(1, 10)))) 
=> 165 
-1

No non c'è, ma è abbastanza facile scrivere il proprio se necessario.