缺点框实施

时间:2014-06-15 10:43:14

标签: ruby scheme

我似乎无法想办法在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

我可以传递两个值并调用carcdr,但这不是任何类型的列表(只有两个值)。如何制作一个列表,我可以在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)但是我没有考虑到我仍然无法用缺点来构建数组,我只是自己做了。

3 个答案:

答案 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 ,以及我在这里使用的内容。)我们将重新打开NilClassCons来完成这项工作:

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