如何在Ruby中实现Common Lisp的mapcar?

时间:2013-08-08 14:42:27

标签: ruby

我想在Ruby中实现Lisp的mapcar

一厢情愿的语法:

mul = -> (*args) { args.reduce(:*) }

mapcar(mul, [1,2,3], [4,5], [6]) would yield [24, nil, nil].

这是我能想到的解决方案:

arrs[0].zip(arrs[1], arrs[2]) => [[1, 4, 6], [2, 5, nil], [3, nil, nil]]

然后我可以:

[[1, 4, 6], [2, 5, nil], [3, nil, nil]].map do |e| 
  e.reduce(&mul) unless e.include?(nil)
end

=> [24, nil, nil]

但是我被困在zip部分。如果输入为[[1], [1,2], [1,2,3], [1,2,3,4]],则zip部分需要更改为:

arrs[0].zip(arrs[1], arrs[2], arrs[3])

对于两个输入数组,我可以这样写:

def mapcar2(fn, *arrs)
  return [] if arrs.empty? or arrs.include? []
  arrs[0].zip(arrs[1]).map do |e|
    e.reduce(&fn) unless e.include? nil
  end.compact
end

但我不知道如何超越两个阵列:

def mapcar(fn, *arrs)
  # Do not know how to abstract this
  # zipped = arrs[0].zip(arrs[1], arrs[2]..., arrs[n-1])
  # where n is the size of arrs
  zipped.map do |e| 
    e.reduce(&fn) unless e.include?(nil)
  end.compact
end

有人有任何建议吗?

2 个答案:

答案 0 :(得分:1)

如果我正确地提出了您的问题,您只需要:

arrs = [[1,2], [3,4], [5,6]]
zipped = arrs[0].zip(*arrs[1..-1])
# => [[1, 3, 5], [2, 4, 6]] 

或者更好的选择,IHMO:

zipped = arrs.first.zip(*arrs.drop(1))

如果arrs内的所有数组长度相同,则可以使用transpose方法:

arrs = [[1,2], [3,4], [5,6]]
arrs.transpose
# => [[1, 3, 5], [2, 4, 6]] 

答案 1 :(得分:0)

根据toro2k,Ruby中可能的mapcar实现之一:

def mapcar(fn, *arrs)
  return [] if arrs.empty? or arrs.include? []
  transposed = if arrs.all? { |a| arrs.first.size == a.size }
                 arrs.transpose
               else
                 arrs[0].zip(*arrs.drop(1))
               end
  transposed.map do |e|
    e.collect(&fn) unless e.include? nil
  end.compact!
end