【问题标题】:passing functions as arguments in ruby在 ruby​​ 中将函数作为参数传递
【发布时间】:2010-10-01 10:51:01
【问题描述】:

我正在尝试用 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 functional-programming closures


    【解决方案1】:

    嗯,你说你知道 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 }
    

    不,我也不觉得它漂亮。

    edit - #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
    

    【讨论】:

      【解决方案2】:

      一个非常幼稚的方法:

      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
      

      【讨论】:

        【解决方案3】:

        听起来你可能还想要 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。

        【讨论】:

        • 您的代码是正确的,但它只是组成了这一部分:{|x, y| x+y} 写起来更快(并且可以说更具可读性)。但是您仍然缺少组合和收集部分。
        【解决方案4】:

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

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

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

        【讨论】:

          猜你喜欢
          • 2015-04-07
          • 2016-11-02
          • 1970-01-01
          • 2015-05-01
          • 1970-01-01
          • 1970-01-01
          • 2014-01-12
          • 2010-10-06
          • 2015-09-15
          相关资源
          最近更新 更多