これは、標準 ML でそのような関数を記述する最も慣用的な方法ではありません。別の方法で書くと、それがどのように機能するかをよりよく理解できると思います。関数valOf
、hd
およびtl
はいわゆる部分関数NONE
であり、それらを使用する唯一の言い訳は、入力がそれぞれまたはでないことを保証する[]
場合です (この場合、プログラムは例外を呼び出します)。
valOf
, hd
orを使用すると、リストが空 (例: ) であるか、オプションが存在するか(例: )tail
を確認する必要があるため、使用の利便性は制限されます。代わりに、パターン マッチング (またはこれらの関数のより堅牢なバージョン) を使用することが望まれます。null xs
isSome ans
以下に、同じ関数を別の 2 つの方法で記述しました。
(* For a list with at least one element in it, check to see if there is a
* greatest element in the tail (xs). If there isn't, then x is the greatest
* element. Otherwise, whichever is the greatest of x and y is the greatest.
*
* This solution is comparable to the one you have above: Find a solution
* for the "smaller" problem (i.e. the reduced list in which x isn't in),
* and then combine the solution. This means making a recursive call to a
* function that doesn't exist at the time of writing it. *)
fun max [] = NONE
| max (x::xs) =
(case max xs of
NONE => SOME x
| SOME y => SOME (Int.max (x, y)))
(* If we were to use a let-expression instead of a case-of, but still restrict
* ourselves from using partial functions, we might make a helper function: *)
fun maxOpt (x, NONE) = SOME x
| maxOpt (x, SOME y) = SOME (Int.max (x, y))
(* Now the essence of the let-expression is boiled down: It finds the largest
* value in the tail, xs, and if it exists, finds the largest of that and x,
* and pass it as a result packed in SOME. *)
fun max [] = NONE
| max (x::xs) =
let val y_opt = max xs
in maxOpt (x, y_opt) end
(* In fact, one can store the largest value so far in the front of the list.
* This is only because the return type is int option, and the input type is
* int list. If the return type were dramatically different, it might not be
* so easy. *)
fun max [] = NONE
| max (x::y::xs) = max (Int.max (x,y)::xs)
| max [x] = SOME x