編碼也快樂:兩隻水壺F#程式

空軍發表於2013-04-14

F# 是微軟 .NET 戰略的一門重要語言,中文的 F# 書籍很少,賈洪峰老師翻譯了一本《C# 與 F# 程式設計實踐》。期望圖靈公司能引進優秀入門教程 Programming F# 3.0,封面故事: http://www.ituring.com.cn/article/31648,封面圖片見文末。

言歸正傳,解釋一下我的 F# 程式(參加編碼也快樂活動第三波):

主函式 pot 的三個引數 t a b 分別是目標水量及兩個壺的容量。

函式 solve 用於解題,引數 p q 是兩個壺的當前水量。
函式第一行列印當前水量;
如果當前水量等於目標水量,成功;
否則,如果 A 壺空,則裝滿它;
否則,如果 B 壺滿,則清空它;
否則,儘可能地從 A 壺往 B 壺倒水;
遞迴地呼叫以上步驟即可完成解題。

函式 gcd 用歐幾里得演算法求兩個整數的最大公約數。

主函式 pot 的第一行首先判斷輸入的引數 t a b 是否超出允許範圍;
接著根據目標水量是不是兩個壺的容量的最大公約數的倍數判斷是否有解;
現在可以置兩個壺的初始水量為零,呼叫函式 solve 0 0 開始解題。

let pot t a b =
  let rec solve p q =
    printfn " -> (%d, %d)" p q
    if p = t || q = t then printfn "-------- OK! --------"
    else if p = 0 then printf "Fill A full"; solve a q
    else if q = b then printf "Empty pot B"; solve p 0
    else
      printf "Pour A to B"
      let x = min p (b - q)
      solve (p - x) (q + x)
  let rec gcd a b = if b = 0 then a else gcd b (a % b)
  if a<1 || b<1 || t<0 || t>a && t>b then printfn "Arg out of range."
  else if t % (gcd a b) <> 0 then printfn "No solve!"
  else printf "Start with "; solve 0 0

以目標水量 3,兩個壺的容量分別為 5 和 6 為引數呼叫 pot 的結果如下:

> pot 3 5 6;;
Start with  -> (0, 0)
Fill A full -> (5, 0)
Pour A to B -> (0, 5)
Fill A full -> (5, 5)
Pour A to B -> (4, 6)
Empty pot B -> (4, 0)
Pour A to B -> (0, 4)
Fill A full -> (5, 4)
Pour A to B -> (3, 6)
-------- OK! --------


相關文章