在Ruby中将函数作为参数传递

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的一些东西,但那感觉非常丑陋。

实现这样的东西,最"Ruby"的方式是什么?

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

或者,如果你想转发参数:

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

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

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(&:+)

声明:我不是 Ruby 程序员。我只是喜欢函数式编程。因此,这可能与 Ruby 风格不太一样。


你的代码是正确的,但它只是让这部分代码:{|x, y| x+y} 更快速地编写(可以说更易读)。但是你仍然缺少组合和收集的部分。 - krusty.ar

0

你可以将方法名作为符号传递,并使用Object#send(或Object#__send__)按名称调用它。(Ruby实际上没有函数,只有方法。)

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

你可以直接通过Object#method检索Method对象,然后传递它们并call它们,但我很少使用这种方式,并且在实践中也很少见到它被使用。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接