我似乎无法想办法在Ruby中使用Scheme的cons框(似乎它几乎都是所有数组)。这是一个非常粗略的概述:
class cons
def initialize (car, cdr)
@car = car
@cdr = cdr
end
#return the car of the pair
def car
return @car
end
#return the cdr of the pair
def cdr
return @cdr
end
end
我可以传递两个值并调用car
和cdr
,但这不是任何类型的列表(只有两个值)。如何制作一个列表,我可以在Scheme中插入一些内容:
myCons = (cons(1, cons(2, cons(3, cons(4, 5)))))
我能找到的最接近的是制作我自己的数组myArray = Array[1, 2, 3, 4, 5]
,然后使用puts myArray.join(' ')
。这只会给我"1 2 3 4 5"
而不是(1 2 3 4 5)
但是我没有考虑到我仍然无法用缺点来构建数组,我只是自己做了。
答案 0 :(得分:3)
您可以声明一个构建数组的方法(to_a
):
class Cons
def initialize(car, cdr)
@car = car
@cdr = cdr
end
attr_reader :car, :cdr
def to_a
list = cdr.respond_to?(:to_a) ? cdr.to_a : cdr
[car, *list]
end
end
myCons = Cons.new(1, Cons.new(2, Cons.new(3, Cons.new(4,5))))
myCons.to_a
# => [1, 2, 3, 4, 5]
此方法检查cdr
是否支持to_a
本身,并在可能的情况下调用它,然后将car
添加到列表的开头,并返回创建的列表。
如果您希望语法类似于cons(1, cons(2,...)
,则可以使用[]
执行此操作:
class Cons
def self.[](car, cdr)
Cons.new(car, cdr)
end
end
现在你可以写:
myCons = Cons[1, Cons[2, Cons[3, Cons[4,5]]]]
答案 1 :(得分:2)
这是Cons
的一种实现,内置了一个不错的打印语法(包括对不正确的列表的支持),并且是可枚举的:
class Cons
include Enumerable
attr_accessor :car, :cdr
class << self
alias [] new
end
def initialize(car, cdr)
self.car = car
self.cdr = cdr
end
def each_pair
return to_enum(:each_pair) unless block_given?
cell = self
while cell.is_a? Cons
yield cell.car, cell.cdr
cell = cell.cdr
end
end
def each
return to_enum unless block_given?
each_pair { |car,| yield car }
end
def print
sb = '('
each_pair do |car, cdr|
sb << yield(car)
case cdr
when Cons
sb << ' '
when nil
else
sb << ' . ' << yield(cdr)
end
end
sb << ')'
end
def to_s
print &:to_s
end
def inspect
print &:inspect
end
end
哦,这是创建列表的简单方法(类似于Common Lisp和Scheme中的list
函数):
def list(*items)
items.reverse_each.reduce(nil) { |result, item| Cons[item, result] }
end
示例:
irb(main):001:0> a = Cons[1, Cons[2, Cons[3, nil]]]
=> (1 2 3)
irb(main):002:0> b = Cons[1, Cons[2, Cons[3, 4]]]
=> (1 2 3 . 4)
irb(main):003:0> a.to_a
=> [1, 2, 3]
irb(main):004:0> a.map(&Math.method(:sqrt))
=> [1.0, 1.4142135623730951, 1.7320508075688772]
irb(main):005:0> list(1, 2, 3, 4, 5)
=> (1 2 3 4 5)
更新:用户写信给我询问如何(除其他外)附加基于cons的列表。作为一个Schemer,我喜欢将cons单元视为不可变的,因此附加的标准方法是将每个元素(从右到左)从左侧列表合并到右侧列表中。以下是我将如何实现它。
首先,让我们定义一个reduce_right
方法。 (从技术上讲,这是一个正确的 fold ,而不是正确的 reduce ,但Ruby更喜欢术语&#34;减少&#34;而不是&#34;折叠&#34 ,以及我在这里使用的内容。)我们将重新打开NilClass
和Cons
来完成这项工作:
class NilClass
def reduce_right(init)
init
end
end
class Cons
def reduce_right(init, &block)
block.call(cdr.reduce_right(init, &block), car)
end
end
然后,append
就像使用reduce_right
一样简单:
def append(lhs, rhs)
lhs.reduce_right(rhs) { |result, item| Cons[item, result] }
end
这允许您附加两个列表,但通常,允许附加任意数量的列表更方便(这是Scheme append
允许的内容):
def append(*lists)
lists.reverse_each.reduce do |result, list|
list.reduce_right(result) { |cur, item| Cons[item, cur] }
end
end
请注意,在这两种情况下,最右边的&#34;列表&#34;不需要是一个正确的列表,你可以通过在那里放置一个不属于缺陷单元的东西来创建不正确的列表:
irb(main):001:0> append(list(1, 2, 3), list(4, 5))
=> (1 2 3 4 5)
irb(main):002:0> append(list(1, 2, 3), list(4, 5), 6)
=> (1 2 3 4 5 . 6)
irb(main):003:0> append(list(1, 2, 3), list(4, 5), list(6))
=> (1 2 3 4 5 6)
(非最右边的列表必须是正确的列表。)
答案 2 :(得分:0)
或者是来自SICP的更好奇的方法:)
def cons(a, b)
lambda { |seek|
seek == 0 ? a : b
}
end
def car(pair)
pair.call(0)
end
def cdr(pair)
pair.call(1)
end