最初に関数を少し書き直してみましょう。
isListOk :: Bool
isListOk = length (filter isItemOk [1 .. 1000]) <= 3
間違いなく、あなたのバージョンよりも慣用的です。(あなたの型シグネチャが間違っていたので、型シグネチャも変更したことに注意してください。さらに、1 .. 1000
ではなく書くべき1.1000
でした。)
遅延評価は、通常、不必要な計算が実行されないようにするため、ここでの最良の友です。
残念ながら、あなたの使用length
(またはリストから各要素を 1 にマッピングし、結果のリストを合計すること) は、ここで邪魔になっています。つまり、length
はリストの背骨で厳密です: リストを最後まで評価した場合にのみ、リストの長さを生成できます。つまり、この場合、プログラムはチェックを 1000 回実行する必要があります。
解決策は、長さの計算 (つまり、リストのスパインの走査) と、計算された長さが特定のしきい値を超えないかどうかのテストを、実際にはそのスパインで遅延している単一の関数に結合することです。引数リスト:
isNotLongerThan :: [a] -> Integer -> Bool
isNotLongerThan [] n = n >= 0
isNotLongerThan (_ : xs) n = n >= 1 && isNotLongerThan xs (n - 1)
そして書く
isListOk :: Bool
isListOk = filter isItemOk [1 .. 1000] `isNotLongerThan` 3
もちろん、再利用可能なソリューションとして、述語としきい値の両方を抽象化できます。
forNoMoreThan :: (a -> Bool) -> Integer -> [a] -> Bool
forNoMoreThan p n = (`isNotLongerThan` n) . filter p
isListOk :: Bool
isListOk = (isItemOk `forNoMoreThan` 3) [1 .. 1000]
最後に、hammar が指摘するように、しきい値が十分に小さく固定されている場合は、単純にパターン マッチングを使用して、リストが十分に短いかどうかを判断できます。