背景故事跟安裝教學什麼的,真的想試的時候自然就會去查,這邊就不浪費篇幅了
defmodule GuessGame do
def play do
number = Enum.random(1..100)
IO.puts("我想了一個 1-100 的數字,請猜猜看!")
guess_loop(number)
end
defp guess_loop(number) do
input =
IO.gets("請輸入你的猜測: ")
|> String.trim()
|> String.to_integer()
case input do
^number ->
IO.puts("恭喜猜中!")
guess when guess < number ->
IO.puts("太小了!")
guess_loop(number)
guess when guess > number ->
IO.puts("太大了!")
guess_loop(number)
end
end
end
GuessGame.play
常常遇到有的朋友看到 Elixir 是 functional (函數式)語言眉頭都皺很緊。
但目前工作寫到的商業邏輯都誅求直覺簡單,沒有用到太多進階的抽象用法,以好讀好維護為主。在這個時候的函數式程式跟物件導向相比就是換個方式整理房間的感覺,不用特別想太多反而很簡單。
我們使用 defmodule
來定義模組,像抽屜、箱子一樣依照實際使用的方式來分類 function。
模組名稱使用大駝峰命名法,如果有巢狀則用 .
來區隔
defmodule SuperWeb.PostController do
# 模組內容
end
後面篇幅會說明 import
與 use
來擴充與重複利用模組的程式
函式必須要在模組裡面被定義,使用 def
與函式名稱定義函式,接著是參數,最後是執行的區塊
def say_hi do
# 如果函式沒有參數,`()` 可以省略
IO.puts("Hi!")
end
def say_hi_to(name) do
IO.puts("Hi, #{name}")
end
每個函數會自動回傳最後一行的結果
注意,Elixir 的函數沒有 return 可以提早送回結果,後續會提這種情況怎麼處理
defp
是定義私有函數(private function),代表這個函數只能在這個模組內部被呼叫。
我們可以使用模組名稱加上 .
再加上函式名稱來呼叫使用函式
GuessGame.play
如果呼叫的對象在同個模組內,可以直接使用函數名稱呼叫,如範例中的 guess_loop(number)
Elixir 裡面,大量的使用了 Pattern matching, 不只是 case
連變數定義與函式呼叫的參數都有使用
magic_number = 3
在使用 =
定義變數的時候
Elixir 會嘗試把右邊的值與結構塞進左邊看看
以 magic_number 這個變數來說,他就是個空白畫布,什麼都能放
行為就變成我們在其他語言常見的 =
定義變數
但也可以有其他很好用的情況
[a, b] = [1, 2]
右邊的樣子擺到左邊,a 為 1,b 為 2%{name: n} = %{name: "Jack", age: 3}
可以只取出需要的欄位並定義變數[head | _rest] = [1, 2, 3]
從 list 中拿第一個123 = 123
沒什麼意義,但是右邊符合左邊,是匹配的狀態,不會報錯case
在 case 或是之後會介紹的同名函式的 pattern matching 中,Elixir 會從上到下依序嘗試匹配
在 case 裡,會拿 case 右邊的值一個一個比對 ->
左邊的模式 (pattern)
如果匹配成功就執行與回傳 ->
右邊的程式碼
case 5 do
3 -> "不會匹配"
true -> "不會匹配"
5 -> "匹配"
end
case 5 do
number -> "會匹配,作用如同 number = 5"
3 -> "上面已經用變數匹配掉了,這一行永遠不會執行"
end
在 case 裡可以使用 when
來額外增加條件,稱為 guard clause 或是 guard
case 5 do
n when n < 3 -> "雖然 n 被匹配,但是 5 < 3 不會過 guard"
n -> "在這一行的 n 匹配"
end
|>
pipe operator (管道運算符?)程式碼中有一段處理使用者輸入的字串,並轉成數字的邏輯使用了 pipe
IO.gets("請輸入你的猜測: ")
|> String.trim()
|> String.to_integer()
如果不使用 pipe 的話,會多了一些其實不必要的變數,是這樣:
input = IO.gets("請輸入你的猜測: ")
trimed_input = String.trim(input)
String.to_integer(trimed_input)
顧名思義,把連續的函式像水管一樣連接|>
會把左邊的值當作右邊函式的第一個參數
"Jack" |> say_hi()
等同於 say_hi("Jack")
雖然不是一定必要
但是在很多連續處理同一個資料的時候可以讓城市非常好讀好維護
對我來說 Elixir 有著類似 Ruby 讀起來像英文的易讀性,但不一樣的是有著表象 (Explicit) 的風格,會適當的不藏東西都寫出來,這點再加上函數式的特性讓寫程式時的函式來源或是套件查詢非常直覺。