iT邦幫忙

2025 iThome 鐵人賽

DAY 1
0
Software Development

通勤看手機就可讀懂的 Elixir 語言入門教學系列 第 1

Elixir 基本語法,看完這一篇就懂五成

  • 分享至 

  • xImage
  •  

背景故事跟安裝教學什麼的,真的想試的時候自然就會去查,這邊就不浪費篇幅了

先來看一小段 Elixir 程式先

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

defmodule 定義模組

常常遇到有的朋友看到 Elixir 是 functional (函數式)語言眉頭都皺很緊。
但目前工作寫到的商業邏輯都誅求直覺簡單,沒有用到太多進階的抽象用法,以好讀好維護為主。在這個時候的函數式程式跟物件導向相比就是換個方式整理房間的感覺,不用特別想太多反而很簡單。

我們使用 defmodule 來定義模組,像抽屜、箱子一樣依照實際使用的方式來分類 function。

模組名稱使用大駝峰命名法,如果有巢狀則用 . 來區隔

defmodule SuperWeb.PostController do
  # 模組內容
end

後面篇幅會說明 importuse 來擴充與重複利用模組的程式

def 定義函式

函式必須要在模組裡面被定義,使用 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)

Pattern matching (模式匹配)

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) 的風格,會適當的不藏東西都寫出來,這點再加上函數式的特性讓寫程式時的函式來源或是套件查詢非常直覺。


下一篇
過一遍常用的基本型態
系列文
通勤看手機就可讀懂的 Elixir 語言入門教學4
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言