这里介绍 Dave 在 14 年的演讲,他归纳了 Elixir 的两个他认为的要点,重新思考编程。分别是:
我个人觉得模式匹配可以减轻程序员的工作,数据转换可以改善维护,以及增加对并发的支持,也不一定对,是一个初浅的思考吧。看你是否同意他的看法。
视频链接需要的话自己搜索吧,标题:Think Different。
其实这两种东西就是表达式(数据转换)和逆表达式(模式匹配)。前者是 fp 的基石,后者则是前者的解释,或者说是解构过程。
虽然说是这么说,但是对于新手而言后者明显要比前者难理解,特别是后者经常会跟递归一起出现...
#1 楼 @blacktulip 我觉得还好,稍微适应下,感觉代码思路很清楚。比如他演示的例子:
defmodule RLE do
def encode(list), do: _encode(list, [])
defp _encode([], result), do: Enum.reverse(result)
defp _encode([{a,n}, a|tail], result) do
_encode([{a,n+1}|tail], result)
end
defp _encode([a,a|tail], result) do
_encode([{a,2}|tail], result)
end
defp _encode([ a | tail ], result) do
_encode(tail, [ a | result ])
end
end
IO.puts inspect RLE.encode([1,2,2,2,3]) #[1,{2,3},3]
你不信用 Ruby 实现来对比看,如何?
#3 楼 @chenge 模式匹配是不符合 ruby 哲学的,模式匹配相当于把对象剖开,但是 OO 哲学里对象内部对于外界都是不可见的,一切操作要以消息传递实现,就像现实中你不能随便拆开一个人的大脑查看。
[a,b] = [1,2]
这种语法糖,你已经钦定了一种数组实现,但是 OO 中数组只是一种能反应:[]
消息的对象。
类似地,你要如何实现 ActiveRecord::Relation 的模式匹配呢?
当你{total_count, records} = relation
,你已经在relation.instance_eval{|x| @total_count}
了,问题是,你怎么知道 relation 里有@records
和@total_count
?
当然,ruby 里也不是没有模式匹配,线性表的(a, (b, c)) = [1,[2,3]]
是没问题的。
还有 @Artoria 的面对接口的模式匹配:
OO 的哲学是模拟,FP 的哲学是推导。FP 喜欢赤裸着的纯数据,而 OO 则把每个对象看成“有自己主见的人”。二者的比较其实没有什么意义,完全在于你怎么看世界。纯数据操作当然是 FP 更方便,但是类似游戏这种模拟领域,OO 的好处就非常明显了。
module RLE
def self.encode(list)
list.each_with_object([]) do |n, result|
if result[-1] == n
result[-1] = [n, 2]
elsif result[-1].is_a?(Array) && result[-1][0] == n
result[-1][1] += 1
else
result << n
end
end
end
end
递归版就不写了,因为性能还不如循环。
代码量有差别吗?差别不大。哪种写法难懂?都难懂,因为逻辑就是那么复杂。但我认为大部分人会觉得模式匹配 + 递归更难懂一点,因为人脑没有尾递归优化,耗费的脑细胞要多一些。
学多点语言很好,有时也要脱离原本的思维去想,这样更容易接受新思维。但也要知道,布道者给的例子都是经过精细设计的,总能找到些场景,用这个工具很方便,用别的工具很麻烦。Facebook 的 AI 团队用 Haskell 肯定是有道理的。
但是一不留神就掉进布道者的圈套里了,以偏概全,好像没了一些特性就什么都干不了了。上面的 Ruby 版很麻烦吗?不就是一个把条件写到方法参数,一个把条件写到方法体内,Elixir 版还多了一个选择分支——写递归的时候真要好好考虑清楚有没有写漏分支。模式匹配也不能做所有事,不然 Elixir 里面也不会提供 case
cond
if
控制结构了。
我去年就把 Elixir/Erlang 过了一遍,虽然没写生产应用,但也学到不少东西,像模式匹配、轻量进程 & 消息、OTP,这样在选型的时候多一种选择,也可以在原有工具上做些改进。我相信很多人都经常学新语言,只是没那么狂热表现出来。Ruby 一路以来都借鉴了不少其它语言的设计,同时要向前兼容——不兼容的灾难已经在别的语言看过了。如果学过 Elixir/Erlang、Go、Rust,那么应该很容易理解 ko1 演讲上谈到的 immutable object、channel、membership 等概念。
但是切勿邯郸学步,精髓没学到,原本怎么走路给忘了。
#9 楼 @jackalcooper 我讲模式匹配你又跟我讲 actor。。erlang 那点东西我又不是不知道,ruby 的对象和 actor 本来就是等同的抽象,ruby 的send
就是 erl 的!
,ruby 有私有状态,actor 的尾递归就不是私有状态了?你要不要记 pid?还有不知道你说的类变量是什么,我写 ruby 没用过类变量。
Elixir 肯定更适合解决某些问题,不然就没存在的意义了。 Elixir 版的 RLE.encode 感觉比传统算法还难懂一些,如果找一个传统算法很麻烦,Elixir 轻松解决的例子就更有说服力了。
module RLE
def self.encode(list)
list.each_with_object([]) { |n, arr|
arr << [n, 0] if arr.empty? || arr.last[0] != n
arr.last[1] += 1
}.map { |x| x[1]>1 ? x : x[0] }
end
end