5

我正在尝试用 ruby​​ 进行函数式编程,但那里似乎没有太多好的文档。

本质上,我正在尝试编写一个具有 Haskell 类型签名的组合函数:

[a] -> [a] -> (a -> a -> a) -> [a]

所以

combine([1,2,3], [2,3,4], plus_func) => [3,5,7]
combine([1,2,3], [2,3,4], multiply_func) => [2,6,12]

等等

我发现了一些关于使用 zip 和 map 的东西,但感觉真的很难用。

实现这样的事情的最“红宝石”方式是什么?

4

4 回答 4

10

好吧,你说你知道 zip 和 map 所以这可能没有帮助。但我会发布以防万一。

def combine a, b
    a.zip(b).map { |i| yield i[0], i[1] }
end

puts combine([1,2,3], [2,3,4]) { |i, j| i+j }

不,我也不觉得它漂亮。

编辑 - #ruby-lang @ irc.freenode.net 建议:

def combine(a, b, &block)
    a.zip(b).map(&block)
end

或者这个,如果你想转发 args:

def combine(a, b, *args, &block)
    a.zip(b, *args).map(&block)
end
于 2009-01-14T19:27:57.030 回答
3

一个非常幼稚的方法:

def combine(a1, a2)
  i = 0
  result = []
  while a1[i] && a2[i]
    result << yield(a1[i], a2[i])
    i+=1
  end
  result
end

sum = combine([1,2,3], [2,3,4]) {|x,y| x+y}
prod = combine([1,2,3], [2,3,4]) {|x,y| x*y}

p sum, prod

=>
[3, 5, 7]
[2, 6, 12]

并带有任意参数:

def combine(*args)
  i = 0
  result = []
  while args.all?{|a| a[i]}
    result << yield(*(args.map{|a| a[i]}))
    i+=1
  end
  result
end

编辑:我赞成 zip/map 解决方案,但这里有一点改进,它有什么难看的?

def combine(*args)
  args.first.zip(*args[1..-1]).map {|a| yield a}
end

sum = combine([1,2,3], [2,3,4], [3,4,5]) {|ary| ary.inject{|t,v| t+=v}}
prod = combine([1,2,3], [2,3,4], [3,4,5]) {|ary| ary.inject(1){|t,v| t*=v}}
p sum, prod
于 2009-01-14T19:22:56.947 回答
1

您听起来可能还想要 Symbol.to_proc (Raganwald 的代码

class Symbol
  # Turns the symbol into a simple proc, which is especially useful for enumerations. 
  def to_proc
    Proc.new { |*args| args.shift.__send__(self, *args) }
  end
end

现在你可以这样做:

(1..100).inject(&:+)

免责声明:我不是 Rubyist。我只是喜欢函数式编程。所以这很可能不像Ruby。

于 2009-01-16T22:42:31.500 回答
0

您可以将方法的名称作为符号传递,并使用Object#send(或Object#__send__)按名称调用它。(Ruby 没有真正的函数,它有方法。)

您可以传递一个lambda或块,它在您想要的参数上调用您想要的方法。传递块可能是首选的 Ruby 方式,当它起作用时(即当你只有一个块要传递时)。

Method您直接通过检索对象Object#method,然后将它们传递给call它们,但是我没有这样做的经验,并且在实践中没有看到它做太多。

于 2009-01-14T19:31:39.347 回答