你的程式碼聞起來很臭嗎?不用擔心!大夫幫你...越治越臭

這是 w3HexSchool 鼠年全馬鐵人挑戰 Week 3 唷

這是 Elixir 從零開始 系列 02 唷

前言

在前一篇裡面我們有提到 interactive shell,在電腦操作上我們可以透過在終端機上輸入 iex 來進入並進行一行一行的操作,但是我們也不可能寫程式的時候都打一行等結果再打下一行吧,終究我們需要用到一些方法來把一整段的程式碼包在一起批次執行,而這些方法就會是今天要介紹的 Function & Module

Function

定義

在函式(Function)命名上面,通常我們開頭以小寫字母或底線開頭,中間任意混搭並配合數個底線區隔字句意義,最後結尾可以為?或!通常來講結尾為!代表這函式可能有 runtime error,結尾為?代表這函式回傳 true or false,當然,以上講的都是通則,並不是一定

以下為一些命名範例

  • rectangle_area
  • cal_the_result
  • output_2_screen
  • test!
  • is_balance?

在使用函式上面,我們開頭使用 def,並且利用 do...end 來包含邏輯部分

1
2
3
4
5
defmodule Geometry do
def rectangle_area(a, b) do
a * b
end
end

def 表示公開函式,當然就另外還會有 defp 表示私有函式囉

在沒有參數的函式上,使用如下

1
2
3
4
5
defmodule Program do
def run do
...
end
end

如果函式本身很短,那我可以寫成這樣子

1
2
3
defmodule Geometry do
def rectangle_area(a, b), do: a * b
end

回傳值

在 Elixir 的世界裡面,所有的東西都回傳值,當然函式也不例外

在函式中,最後一行程式碼就是這個函式的回傳值喔

1
2
3
4
5
defmodule Geometry do
def rectangle_area(a, b) do
a * b # Calculates the area and returns the result
end
end

參數(arity)

從前抓一個例子回來看

1
2
3
4
5
defmodule Geometry do
def rectangle_area(a, b) do
a * b
end
end

這個函式 rectangle_area 它接收兩個參數 ab,在 Elixir 的世界中我們稱呼為 Geometry.rectangle_area/2,我們稱呼數字為參數數目,這個參數數目很重要也不可省略,因為同樣名稱可以有不同的參數數目

如下例子,我們同時間有 Rectangle.area/1 和 Rectangle.area/2,這兩個函式是截然不同的!

1
2
3
4
5
defmodule Rectangle do
def area(a), do: area(a, a)

def area(a, b), do: a * b
end

iex 中是這樣的

1
2
3
4
5
iex(1)> Rectangle.area(5)
25

iex(2)> Rectangle.area(5,6)
30

我們也可以為函式設定預設參數,用法是在參數後面加 \\ 符號

1
2
3
4
5
defmodule MyModule do
def fun(a, b \\ 1, c, d \\ 2) do
a + b + c + d
end
end

這個函式本身包含了 MyModule.fun/2,MyModule.fun/3 和 MyModule.fun/4 喔

匿名函式(Anonymous Functions)

在前面所講的都是所謂的 Named Functions,現在來介紹一下匿名函式或是稱為 lambda,使用方法會需要 fnend 兩個關鍵字,然後使用 -> 來定義函式本體

1
2
3
iex(1)> sum = fn a, b -> a + b end
iex(2)> sum.(2, 3)
5

或者我們也可簡化用法,使用 & 符號

1
2
3
iex(1)> sum = &(&1 + &2)
iex(2)> sum.(2, 3)
5

這裡我們講兩個小細節

第一小細節,我們可以看到為什麼都要加一個 dot(.),通常來講加了 dot(.) 是表示這是一個匿名函式 sum.(2, 3),沒有加的表示命名函式 sum(2, 3),這是為了增加程式碼的可讀性

第二個小細節,匿名函式的參數建議不要用括號

1
2
3
iex(1)> square = fn x ->
x * x
end

命名函式的參數建議用括號

1
2
3
4
5
defmodule Geometry do
def rectangle_area(a, b) do
a * b
end
end

為什麼呢?賣個關子,我們在之後會說明喔!

Module

定義

我們定義模組(Module)就是一堆函式集合在一起,而函式只能在模組之中被定義

在命名上面,我們通常開頭大寫並且使用 CamelCase style,這 style 不懂可以去 Google 一下,而模組名稱可以有底線和 dot(.) 後者經常用來做結構區分,我們來舉一些命名例子

  • Geometry.Rectangle
  • Module6
  • DoSomething

在 Elixir 中有很多已經定義好的模組可以使用唷,比如說 IO 模組,它是用來針對 I/O 做操作,此模組裡面的一個函式 puts 可以在螢幕中印出訊息

1
2
3
iex(1)> IO.puts("Hello World!")
Hello World!
:ok

我們可以看到呼叫模組的語法如下

ModuleName.function_name(args)

:ok 則是 IO.puts 的回傳值

在使用模組上面,我們開頭使用 defmodule,並且利用 do...end 來包含各個函式

1
2
3
4
5
defmodule Geometry do
def rectangle_area(a, b) do
a * b
end
end

當然我們可以有多個函式在同一個模組

1
2
3
4
5
6
7
8
9
defmodule Geometry do
def rectangle_area(a, b) do
a * b
end

def square_area(a) do
rectangle_area(a, a)
end
end

一個檔案中可以有多個模組

1
2
3
4
5
6
7
defmodule Module1 do
...
end

defmodule Module2 do
...
end

或是我們之前提到的 dot(.)

1
2
3
4
5
6
7
defmodule Geometry.Rectangle do
...
end

defmodule Geometry.Circle do
...
end

還有階層式的模組關係

1
2
3
4
5
6
defmodule Geometry do
defmodule Rectangle do
...
end
...
end

注意喔!上面這個關係根據 Elixir in Action 的解釋如下,這部分我也看不太懂,先存起來 XD

The child module can be referenced with Geometry.Rectangle. Again, this nesting is

a convenience. After the compilation, there’s no special relation between the modules

Geometry and Geometry.Rectangle.

引用和別名(Imports and aliases)

話不多說,直接看引用的範例

1
2
3
4
5
6
7
defmodule MyModule do
import IO # here we import the module IO

def my_function do
puts "Calling imported function." # here we use puts instead of IO.puts
end
end

來看一下別名的範例

1
2
3
4
5
6
7
defmodule MyModule do
alias Geometry.Rectangle, as: Rectangle # Sets up an alias to a module

def my_function do
Rectangle.area(...) # Calls a module function using the alias
end
end

在上面的例子中我們使用別名

1
alias Geometry.Rectangle, as: Rectangle # Sets up an alias to a module

Elixir 提供了一個甜甜的語法糖,程式可以直接擷取最後一部分當作是別名,這樣可以偷懶一些字不用打,如下

1
alias Geometry.Rectangle # Sets up an alias to a module

上面的別名一樣是 Rectangle 喔

屬性(Module attributes)

Module attributes 這個有點類似 C# 中的類別Property,定義好之後就可以在模組內使用,我們用 @ 這個符號來新增和使用屬性,另外 Elixir 也預先定義了一些像是 @moduledoc@doc 等,這裡我不多介紹,程式參考如下

1
2
3
4
5
defmodule Circle do
@pi 3.14159
def area(r), do: r * r * @pi
def circumference(r), do: 2 * r * @pi
end

這裡有一點要提醒的,不管是 defmodule 或是 def 它們兩個都不是 Elixir 的關鍵字(keywords),他們都只是 macros 而已唷

如何使用

上面寫的落落長,但是,但是

寫好一個模組或函式之後呢,那我們要怎麼樣使用它?

我們可以建立一個副檔名為 ex 的檔案(對!Elixir的 source file 都是這個副檔名唷)並且把上面的那些程式碼丟到裡面去,這一個一個的 ex 檔案都會像是副程式一樣的存在

那在 shell 底下怎麼測試呢,參考如下

1
2
3
$ iex geometry.ex
iex(1)> Geometry.rectangle_area(6, 7)
42

參考資料

  1. https://elixirschool.com/en/lessons/basics/modules/
  2. https://elixir-lang.org/getting-started/module-attributes.html

這是 w3HexSchool 鼠年全馬鐵人挑戰 Week 2 唷

這是 Elixir 從零開始 系列 01 唷

前言

Elixir 是什麼?它於 1983 年誕生於日本,帶著先進膠原蛋白保養技術問世。

https://www.elixirtw.com/zh-TW/about_story/index

誒誒誒?是不是走錯棚了吧?

相信大家第一個 Google 到的都是保養品網站

不過這不是我們要的

記得在輸入關鍵字的時候多打一個 lang 也就是 elixir lang

就像搜尋 Go 語言大家會打 golang 一樣,比較不會被 Google 誤會你要 go 去哪裡啦

What is Elixir

依照維基百科的定義

Elixir 是一個基於 Erlang 虛擬機器函數式、面向並列的通用程式語言。Elixir 以 Erlang 為基礎,支援分散式、高容錯、即時應用程式的開發,亦可通過巨集實現元程式設計對其進行擴充,並通過協定支援多型[1]

José Valim 是 Elixir 語言的設計者。他創造該語言的目標是在維持與現有 Erlang 工具鏈及生態環境相容性的同時,讓人們可以在 Erlang 虛擬機器上進行擴充性更好的、高生產率的開發。[2]

簡單來講 Elixir

  • 一個基於 Erlang 的新語言,提供更好的程式開發效率
  • 是 Functional programming
  • 設計者為 José Valim

優點:這邊可以說是繼承了 Erlang 的優點,Erlang 什麼地方好棒棒 Elixir 就一樣好棒棒

這裡提供 Elixir in Action 這本書提出關於 Erlang 的優點

Fault-tolerance—A system has to keep working when something unforeseen happens.

Unexpected errors occur, bugs creep in, components occasionally fail, network

connections drop, or the entire machine where the system is running crashes.

Whatever happens, you want to localize the impact of an error as much as possible,

recover from the error, and keep the system running and providing service.

Scalability—A system should be able to handle any possible load. Of course, you

don’t buy tons of hardware just in case the entire planet’s population might start

using your system some day. But you should be able to respond to a load increase

by adding more hardware resources without any software intervention. Ideally,

this should be possible without a system restart.

Distribution—To make a system that never stops, you have to run it on multiple

machines. This promotes the overall stability of the system: if a machine is taken

down, another one can take over. Furthermore, this gives you the means to scale

horizontally — you can address load increase by adding more machines to the

system, thus adding work units to support the higher demand.

Responsiveness—It goes without saying that a system should always be reasonably fast

and responsive. Request handling shouldn’t be drastically prolonged, even if the

load increases or unexpected errors happen. In particular, occasional lengthy tasks

shouldn’t block the rest of the system or have a significant effect on performance.

Live update—In some cases, you may want to push a new version of your software

without restarting any servers. For example, in a telephone system, you don’t

want to disconnect established calls while you upgrade the software.

Erlang 是由瑞典的 Ericsson 電信公司發明的,電信公司嘛講求的就是服務不中斷,由上述幾點也可以印證出了服務不中斷的精神喔

Installing Elixir

這邊我們要開始架設環境練練手了!

如果你會用 Docker 那其實很快

1
docker run -it --rm elixir

好了後就可以直接使用了,不需要接下來的設定囉

~~

如果你在 mac 環境底下並且已經安裝了 brew

1
brew update
1
brew install elixir

Setting PATH environment variable

我們來找一下我們剛剛裝在什麼地方

1
2
$ where elixir
/usr/local/bin/elixir

接下來就是把路徑設定到你的 shell profile 裡面

1
export PATH="$PATH:/usr/local/bin/elixir"

好了後我們重新開一個新的終端機下

1
2
3
4
$ elixir --version
Erlang/OTP 22 [erts-10.6.2] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:1] [hipe] [dtrace]

Elixir 1.10.0 (compiled with Erlang/OTP 22)

完成!

Hello World

每個程式最一開始當然不免俗的要來一個 Hello World 一下

最簡單的方法就是使用 interactive shell

在這模式裡面你可以下任何的 Elixir 指令並即時看其結果

1
2
3
4
5
6
7
8
$ iex

Erlang/OTP 22 [erts-10.6.2] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:1] [hipe] [dtrace]

Interactive Elixir (1.10.0) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> "Hello " <> "World"
"Hello World"
iex(2)>

後記

最一開始碰 Elixir 其實是強者我同學告訴我的,但那時候滿腦子都是 OOP 就沒有多著墨,剛好最近看到六角學院看到有一個活動 w3HexSchool 鼠年全馬鐵人挑戰的活動,想到以前碰過一下下就丟角落,最近趁這個活動來嘗試一下 Functional programming 的威能啊!

參考資料

  1. https://elixir-lang.org/getting-started/introduction.html

前言

偶然在六角學院看到有一個活動

w3HexSchool 鼠年全馬鐵人挑戰

內容是一週寫一篇技術文章

獎項為:

  • 金角獎 (實體獎座)
    • 成功撰寫滿 40 週者均可獲得。
  • 銀角獎 (實體獎座)
    • 成功撰寫滿 25 週者均可獲得。
  • 銅角獎 (數位獎狀)
    • 成功撰寫滿 10 週者均可獲得。

想說從小讀書沒拿過獎盃獎狀,這個只要騙吃騙喝(喂!)就可以有獎座

再加上最近想要學習一些新的東西,希望可以透過寫文章的方式加深學習的印象,也方便事後復習

學新東西+獎盃,兩個願望一次滿足聽起來很棒吧!

於是乎

參加起來!!!

預計這系列的文章主題可能會有以下主題,任一

Git

Elixir

Data Structures + Algorithms

參考資料

  1. https://www.hexschool.com/2019/11/14/2019-11-14-w3Hexschool-2020-challenge/

前言

寫一些文章記錄程式筆記,比較了很多寫 Note 軟體,看來看去,還是網頁比較可以調出漂亮的程式碼區塊壓

GitLab Pages: GitLab Pages is a feature that allows you to publish static websites directly from a repository in GitLab.

Hexo: A fast, simple & powerful blog framework, powered by Node.js.

Node.js 安裝

在全新安裝的 Ubuntu 18.04 環境底下執行以下指令

1
2
3
$ sudo apt-get install curl
$ curl -sL https://deb.nodesource.com/setup_10.x | sudo -E bash -
$ sudo apt-get install nodejs

接下來執行以下指令,我們來來看一下安裝好的版本

1
2
3
4
$ node -v
v10.18.1
$ npm -v
6.13.4

注意:如果你是使用 Windows Subsystem for Linux 的童鞋,那你有很大的機率遇到以下問題,如果沒遇到,恭喜你,可以直接跳到下一節

1
2
3
4
$ npm -v
: not foundram Files/nodejs/npm: 3: /mnt/c/Program Files/nodejs/npm:
: not foundram Files/nodejs/npm: 5: /mnt/c/Program Files/nodejs/npm:
/mnt/c/Program Files/nodejs/npm: 6: /mnt/c/Program Files/nodejs/npm: Syntax error: word unexpected (expecting "in")

別緊張,首先我們先來看一下 npm 的位置在哪裡

1
2
$ which npm
/usr/bin/npm

我們可以看到 npm 是位於 /usr/bin/npm 這個位置,但是上面的錯誤提示寫說我在 Program Files 裡面找不到 npm 阿

所以我們要在 PATH 裡面增加 /usr/bin,注意這個新增必須要在 $PATH 之前才會生效

打開 ~/.profile,然後把 PATH 的地方用以下的指令取代

1
PATH="$HOME/bin:$HOME/.local/bin:/usr/bin:$PATH"

最後要記得 source ~/.profile

什麼都做好之後呢,重新開一個新的 console 來試試看囉

1
2
3
4
5
6
$ whereis npm
npm: /usr/bin/npm /mnt/c/Program Files/nodejs/npm /mnt/c/Program Files/nodejs/npm.cmd
$ node -v
v10.18.1
$ npm
6.13.4

現在 Node.js + npm 都己經安裝完成啦

Hexo 安裝

一旦所有的必備軟體都安裝完畢後,即可透過 npm 安裝 Hexo

1
2
$ npm install -g hexo-cli
$ hexo init hexo

完成後可以看到 hexo 資料夾內有下列檔案(每個人看到的可能大同小異)

1
2
3
4
5
6
7
8
9
10
11
12
.
├ _config.yml
├ package-lock.json
├ package.json
├ scaffolds
│ ├ draft.md
│ ├ page.md
│ └ post.md
source
│ └ _posts
└ themes
└ landscape

我們可以執行指令來開啟一個本地端的伺服器,預設是 http://localhost:4000/

1
$ hexo s

Hexo 主題

預設的部落格雛形長得又老又醜,想當然我們會想要趕快換掉它,以免傷眼睛!

Hexo 有很多的主題包可以自行下載來使用

我這邊使用的是 NexT ,依照它的安裝建議來安裝即可

1
2
$ cd hexo
$ git clone https://github.com/theme-next/hexo-theme-next themes/next

注意:這邊 themes/next 請記得改成 themes/landscape,之後會提到為什麼

下載好之後呢,再次打開本地端伺服器看一下

1
$ hexo s

新的主題就會完美呈現!

GitLab Pages 部署

到這裡只剩下最後一步了,那就是上傳整個部落格到 GitLab 上,讓別人可以看到網站!

我們要回到原本建立的資料夾

1
$ cd ~/hexo/

我們新建一個檔案 .gitlab-ci.yml 貼上以下內容,這個檔案的作用是告訴 GitLab CI 做自動化部屬的動作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
image: node:10.16.0
cache:
paths:
- node_modules/

before_script:
- npm install hexo-cli -g
- test -e package.json && npm install
- hexo clean
- hexo generate

pages:
script:
- hexo generate
artifacts:
paths:
- public
only:
- master

再來

我們先下載 Git 套件並設定 config

1
2
3
4
$ sudo apt-get install git
$ git config --global user.name "你的GitLab用户名"
$ git config --global user.email "你的GitLab註冊郵箱"
$ ssh-keygen -t rsa -C "你的GitHub註冊郵箱"

好了之後我們到 ~/.ssh 內找到 id_rsa.pub,把它複製貼上到 GitLab 設定裡面的SSH Keys 內

完成之後呢我們就可以對 GitLab 上 Git commit/push 啦~

我們到 GitLab 建立一個新的專案

請注意以下幾點

  1. 新建一個專案 Project name 請務必填入 <<您的帳號>>.gitlab.io
  2. Visibility Level 建議設成公開的
  3. 專案之設定 Visibility, project features, permissions 請把 Pages 的權限設定成 Everyone

建好新專案後,我們回到 hexo/ 資料夾

1
2
3
4
5
$ git init
$ git remote add origin git@gitlab.com:<< your account >>/<< your account >>.gitlab.io.git
$ git add .
$ git commit -m "Initial commit"
$ git push -u origin master

大功告成

我們可去網站上看囉!

以下這個將會是你的網址!

https://<< your account >>.gitlab.io/

注意:前面提到把資料夾改成 themes/landscape

我在這邊曾經遇到一個問題,我使用 NexT 這個主題並且在上傳該主題檔案到 GitLab 之後發現該主題的資料夾沒有被上傳,導致我的 gitlab.io 完全沒有東西

網路上相關問題:

远程仓库无法备份theme/next主题的问题

問題 - Hexo 部署上 Github 後頁面全空了

後來找了很久,還是沒有解...

所以我把 next 的所有資料都丟到 landscape 內偽裝成 landscape 主題暫時這樣解決XD

參考資料

  1. https://hexo.io/zh-tw/docs/
  2. https://github.com/theme-next/hexo-theme-next
  3. https://blog.typeart.cc/%E9%83%A8%E7%BD%B2HEXO%E5%88%B0GitLab%20Page/
  4. https://hexo.io/zh-tw/docs/gitlab-pages