(Erlang) 擷取使用者輸入

要從命令列擷取使用者輸入資料,也就是先印出一些提示文字請使用者輸入資料,在Erlang上做得可能比較愉快,尤其是在讀入資料之後,做一些資料格式檢查方面,函數語言的表現會做得比較好。

首先思考擷取使用者輸入的步驟,可依序列為幾項: 1. 定義輸入器接受哪一種資料格式; 2. 啟動輸入器,開始印出提示文字; 3. 輸入器擷取使用者輸入; 4. 輸入器將指定資料格式所對應的格式檢查器啟動,檢查輸入資料。以下,我介紹的是將四種步驟所對應的程式拆成可以彼此銜接的單元,讓程式設計起來比較容易。

假設我的輸入器模組為

-module(io_dialogue).
-compile(export_all).

我可以說 ask_int/1 是擷取一段輸入,定義為

ask_int(Question) ->
 IOMachine = ask([{'Format', fun format:is_int/1}]),
 IOMachine(Question).

定義 ask_int/1 是擷取輸入,它引用了 ask/1 。而 ask/1 是輸入器產生器,將它設定了一些設置項目之後,會產生符合設定項目的輸入器。ask/1 定義為

ask(Options) ->
  fun (Question) ->
    io:write(Question), io:nl(),
    case io:get_line() of
       eof -> stop;
       {error, Reason} -> {error, Reason};
       "\n" -> stop;   %%% 在提示輸入時,直接按Enter會得到"\n"
       Data ->            %%% 普通輸入文字尾端也會補一個"\n"
           case proplists:lookup('Format', Options) of
              none -> stop;
              {'Format', Func} ->
                    Word = string:left(Data, string:len(Data)-1),
                    case Func(Word) of
                       {ok, Value} -> Value;
                       {error, Reason} -> {error, Reason}
                    end
           end
     end
  end.

首先有 io:write(Question), io:nl(), io:get_line() 等呼叫時,會先印出提示訊息 Question ,接著一個行尾符號,然後 io:get_line() 時等待使用者輸入。 ask/1 的設置項目 {‘Format’, Func} , ‘Format’ 要對應一個函數 Func 。 Func 是指一個判斷函數,將資料丟進 Func ,它會回答是 {ok, Value} 或者 {error, Reason} 。像再上方所寫的 format:is_int/1 ,依照 Func 函數規格,則定義為

-module(format).
-export([is_int/1]).
is_int(Data) ->
     try
          {ok, list_to_integer(Data)}
                  %% Erlang裡頭的字串就是 list , list 就是字串
                  %% list_to_integer/1 等於是把字串轉換為整數
     catch
           {error, Reason} -> {error, Reason}
     end.

回頭看 ask_int/1 的定義:

ask_int(Question) ->
      IOMachine = ask([{'Format', fun format:is_int/1}]),
      IOMachine(Question).

它的函數傳回值,根據 ask/1 的定義,是傳回 {ok, Value} 或 {error, Reason} 或 stop 三種值,{ok, Value} 是有合適的資料,{error, Reason} 是讀取資料發生錯誤情況, stop 是讀不到資料。 ask_int/1 的用法是,

Result = io_dialogue:ask_int('What number do you want? '),
case Result of
     {ok, Value} ->
          ......
     {error, Reason} ->
          ......
     stop ->
          ......
end

在三種結果情況中,可以分別定義要做什麼反應。有可能是,在 {error, Reason} 情況時,再做一次 io_dialogue:ask_int(‘What number do you want? ‘) 這個動作。

回頭看開頭所界定的四個輸入步驟:

1. 定義輸入器接受哪一種資料格式:在撰寫 ask/1 和 format:is_int/1 就是在進行這個步驟。 format:is_int/1 這一類檢查用的函數,也可以外加自行撰寫的 parser 來處理檢查工作。

2. 啟動輸入器,開始印出提示文字:在 ask_int/1 中,產生一個輸入器 IOMachine 之後,將提示文字 Question 代入 IOMachine 即啟動輸入器。

3. 輸入器擷取使用者輸入:定義在 ask/1 中,從開始 io:get_line() 即取得資料,之後用 string:left/2 稍微清理資料。如果資料沒有意義,就放出 stop 信號。

4. 輸入器將指定資料格式所對應的格式檢查器啟動,檢查輸入資料:也定義在 ask/1 中,從設置項目中找到 ‘Format’ 指定的函數 Func ,然後用 Func 檢查資料,如果資料正確就放出資料,如果資料不正確就放出錯誤信號。

相當簡單,對不對?

廣告

About 黃耀賢 (Yau-Hsien Huang)

熱愛 Erlang ,並且有相關工作經驗。喜歡程式語言。喜歡邏輯。目前用 Python 工作。
本篇發表於 Erlang, Introduction。將永久鏈結加入書籤。

發表迴響

在下方填入你的資料或按右方圖示以社群網站登入:

WordPress.com Logo

您的留言將使用 WordPress.com 帳號。 登出 / 變更 )

Twitter picture

您的留言將使用 Twitter 帳號。 登出 / 變更 )

Facebook照片

您的留言將使用 Facebook 帳號。 登出 / 變更 )

Google+ photo

您的留言將使用 Google+ 帳號。 登出 / 變更 )

連結到 %s