从function编程的角度来看,Rubyfilter是不同类型的?

在Ruby中,有一些过滤函数会产生与您开始时不同的类型。

例如,如果你这样做

{a: 2, b: 0}.find_all{|key, value| value.zero?} # Use Hash[new_array] to turn it into a hash 

你最终得到一组键和值,而不是另一个哈希。

如果你这样做

 str = "happydays" all_indexes = [1, 2, 7, 8] str.each_char.reject.with_index{|char, index| all_indexes.include?(index)} # Use .join to turn it into a string 

你最终得到一个字符数组,而不是一个字符串。

从函数式编程的角度来看这是正常的,还是仅仅表明Ruby没有完美地实现函数式编程范式?

哪种语言“完美实现函数式编程范式”? Haskell,Erlang,Pure,OCaml,Clojure? 选择你的选择,他们都倾向于完全不同的做事。 我真的不想在这里争论(我运行一个函数式编程用户组,我们喜欢讨论这种类型的东西),但与OOP一样,函数式编程需要有不同的想法。

现在虽然大多数人不会认为Haskell在纯度方面处于领先地位,但这绝不是做FP的唯一方法。 恕我直言,Michael Fogus和Chris Houser在“Clojure的喜悦”中总结得很好:

function编程关注并促进function的应用和组合。 此外,对于一种被认为是function性的语言,其function概念必须是一流的。 必须能够像该语言中的任何其他数据一样存储,传递和返回语言的function。 除了这个核心概念之外,定义分支到无限,但幸运的是,它足以开始。

函数不仅仅是从域到codomain的某种映射,而且两者当然不必相同。 如果你看一个像f(x) = sqrt(x)这样的函数并假设N(自然数)是f的域,那么很明显codomain不会是相同的(除非你想要一个未定义的函数)在大片段。

尽管如此,我认为这种行为并不是特别有问题。 对齐类型(即使我们通常不在Ruby中使用这个术语)是开发人员的责任,而不是语言。 后者可以帮助找到那些不匹配,并且在发现它们时也会有所不同(例如编译时间与运行时间)。

正如Mladen所说,有很多东西阻止Ruby成为一种纯函数式语言,但对于大多数语言来说都是如此,其中不少语言本身就是函数式语言(Clojure通常倾向于使用纯粹的可用性和实用性)。 然而,如果真的想要并注意一些细节,那么很有可能在Ruby中以非常实用的方式编程。 以下是该主题的一些链接:

  • 通过function编程改进Ruby
  • 在Ruby中的function思考(PDF)
  • 在Ruby中的function思考(对话)

根据定义,函数式编程范例避免了状态的变化。 通过这个定义和你给出的例子,我认为Ruby还没有完美地实现这个范例。 它显然并不可怕,因为你有很多function,如地图,折叠(注入),filter等,并支持lambda函数/延迟评估等。

根据设计,Ruby永远不会是纯函数式语言,因为它对命令式/面向对象编程的自然支持。 因此,Ruby的设计者可以做很多事情来平衡这种多范式语言。

希望我们能得到更多答案,因为我也很感兴趣,但这是我的意见:

我不明白为什么一些corelib函数返回的类型意味着语言function较少。 使用任何纯函数式语言,你可以实现一个带有类型A东西并返回类型B的函数,这就是你在那里所拥有的东西。 我们可以在这里讨论上述方法的决定背后的原因,以返回它们返回的内容。

还有其他一些东西阻止Ruby成为一种纯函数式语言(可变性,开始)。

现有的答案已经讨论过(以及很好)Ruby的function性(顺便说一句,我在这里写的可能很有趣)。 现在,回答你的问题:我会说 – 从任何角度来看,不仅仅是FP,只是常识 – filter操作应该总是返回一个具有相同原始类型的对象。 关于您的问题的一些评论:

1)你想知道为什么Hash#find_all (= Hash#select Hash#find_all )返回一个数组。 实际上,这没有任何意义,当Hash#reject确实返回哈希值时就越多。

 >> {:a => 1, :b => 2}.select { |k, v| v > 1 } #=> [[:b, 2]] >> {:a => 1, :b => 2}.reject { |k, v| v > 1 } #=> {:a=>1} 

但这很久以前被认为是一个bug,幸运的是在Ruby 1.9中解决了这个问题:

 >> {:a => 1, :b => 2}.select { |k, v| v > 1 } #=> {:b=>2} # Ruby 1.9 >> {:a => 1, :b => 2}.reject { |k, v| v > 1 } #=> {:a=>1} 

2)你的第二个例子( String#each_char )它实际上与这个问题无关。 此方法返回字符串中字符的可枚举(如果您愿意,“懒惰数组”),因此选择/拒绝/ …在其上返回一个数组,这是正确的。 好吧,为了正统,他们也应该返回一个懒惰的枚举器,但Ruby在这里仍有改进的余地(检查Facets’Denumerator s以查看它应该做的好方法)。

3)@ Ed’ka在讨论中介绍了一个相关且有趣的概念: fmapfmapfmap函数的一个通用版本的map(它只是你可以迭代的容器。 fmap函数的例子:列表,树,关联数组,集合……)。 在Ruby中我们可能想知道Hash#map应该返回什么..一个数组? 哈希? 例如,在Haskell中,一个map只对列表有意义(虽然它具有完全不同的性质,相当于Ruby中的数组),因此Hash#map返回一个数组似乎是可以接受的(另一种方法是强制转换使其成为可能)更清楚: hash.to_a.map { |k, v| ... } )。 顺便说一句,在Ruby中实现Hash#fmap非常简单,我经常使用它并将其包含在我的通用扩展模块中:

 class Hash def fmap(&block) Hash[self.map(&block)] end end {:a => 1, :b => 2}.fmap { |k, v| [k.to_s, v*2] } #=> {"a" => 2, "b" => 4}