【问题标题】:folding and currying with Ruby使用 Ruby 进行折叠和柯里化
【发布时间】:2017-10-11 16:53:38
【问题描述】:

我正在探索使用 Ruby 语言进行函数式编程。下面是我在 Ruby 中的折叠版本。我已经在各种功能、反向、过滤、映射等上对其进行了测试,它按预期返回了结果。但它会改变数据并需要赋值语句。任何人都可以帮助我做同样的事情,但又不违反功能范式吗?谁能帮我部分应用底部的咖喱函数?我怀疑我缺少一些明显的东西。谢谢。

fold_l = lambda do |ray, base, funcky|
    if ray == []
        base
    else
        base = funcky.call(base,ray.first)
        ray.shift
        fold_l.call(ray,base,funcky)
    end
end

abc = [1, 2, 3, 4, 5, 6, 7]
mapper = lambda {|sum, x| sum << x*x}
lengthy = lambda {|sum, _| sum + 1}

p fold_l.call(abc,[],mapper)  ## works fine
p abc                         ## but mutates data!!
abc = [1, 2, 3, 4, 5, 6, 7]

p curryFold = fold_l.curry.(abc).(0).(lengthy) ## works fine
lengthC = curryFold.(base:0).(funcky:lengthy)  
p lengthC.call.(abc)  ## but this gives error

【问题讨论】:

    标签: ruby lambda functional-programming currying


    【解决方案1】:

    重做您的 fold_l 函数以不破坏其给定的参数:

    def fold_l(ray, base, funcky)
      return base if ray.empty?
    
      base = funcky.call(base,ray.first)
    
      fold_l(ray.last(ray.length-1),base,funcky)
    end
    

    这使用last 返回参数的副本减去第一个。这里也没有必要使用lambda,因为你想要一个命名函数,所以你也可以正式声明它。 lambda 保留用于您不一定要为其命名的情况。

    请注意,在 Ruby 中,破坏您的方法给出的参数通常是不礼貌的,除非您理解这是可以接受的。如果需要执行更改,大多数方法都会制作副本。

    【讨论】:

    • 感谢您在使用 'last' 方面的帮助,我注意到 'base = funcky.call(base,ray.first)' 部分可以放入 fold_l 调用的第二个参数中避免赋值语句。咖喱仍然是个问题。根据您的建议,我现在在 BOTH 语句上都遇到错误...
    • 如果这解决了您的参数混乱问题,那么它就是原始问题的解决方案。如果您还有其他问题,值得将其作为一个针对该特定角度的新问题提出。
    • 不仅“破坏论据是粗鲁的”(好文字!),而且我的理解是,函数式编程的基本原则是代码不能有副作用,例如参数的变化。
    【解决方案2】:

    我可能会像这样实现foldl——并且在不支持尾调用优化的语言中使用递归时要小心 (read more)

    foldl = -> (f, acc, (x,*xs)) do
      if x.nil? then
        acc
      else
        foldl.call f, (f.call acc, x), xs
      end
    end
    
    add = -> (x,y) do
      x + y
    end
    
    length =
      foldl.curry
        . (-> (acc,_) { acc + 1 })
        . (0) 
    
    data = [ 1, 2, 3, 4, 5 ]
    
    p foldl.call add, 0, data
    # => 15
    
    p length.call data
    # => 5
    

    根据 @tadman 的建议,使用普通的 def/end 块可能会更好,但这只是一个偏好问题 - 请注意,这种风格不再需要柯里化

    def foldl f, acc, (x,*xs)
      if x.nil? then
        acc
      else
        foldl f, (f.call acc, x), xs
      end
    end
    
    def add x, y
      x + y
    end
    
    def length xs
      foldl (-> (acc,_) { acc + 1 }), 0, xs
    end
    
    data = [ 1, 2, 3, 4, 5 ]
    
    p foldl method(:add), 0, data
    # => 15
    
    p length data
    # => 5
    

    【讨论】:

    • 感谢您的回答。我喜欢 '(x, *xs)' 技巧,尽管 Ruby 语法并没有让它更容易理解。我需要在参数中使用 4 个嵌套括号来运行一个小样本,即:def sample((x,*xs))
    猜你喜欢
    • 2011-08-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-10-15
    • 2010-09-14
    • 1970-01-01
    相关资源
    最近更新 更多