Haskellには次のtakeWhile
機能があります。
Prelude> takeWhile odd [1,3,5,7,8,9]
[1,3,5,7]
述語関数を適用すると結果がTrue
。になる限り、リストから要素を「取得」します。になるFalse
と止まります。
どうすればそれを実装できますか?
これが私が思いついたHaskellの再帰的アプローチです:
takewhile::(a->Bool)->[a]->[a]
takewhile _ [] = []
takewhile f (x:xs) | f x == True = x : takewhile f xs
| otherwise = []
述語f x
がTrue
である限り、それ自体を呼び出し続けます。そうでない場合は、それ自体を呼び出さずに空のリストを返します。
JavaScriptの次の実装を思い付くことができます。これは少し冗長であり、中間結果を渡すために別の関数の定義を呼び出します。
function takeWhile(f, xs) {
return take(f, xs, [])
}
function take(f, xs, arr) {
if(!xs || xs.length === 0) {
return arr
}
x = xs.shift()
if(f(x)) {
arr.push(x)
return take(f, xs, arr)
} else {
return arr
}
}
takeWhile((x)=>{
return x % 2 !== 0
},[1,3,5,7,9,11])
JavaScriptでそれを実装するためのより良いアイデアはありますか?
takeWhile
HSのように、つまり怠惰に実行したい場合は、JSにジェネレーターが必要です。
function* takeWhile(fn, xs) {
for (let x of xs)
if (fn(x))
yield x;
else
break;
}
function* naturalNumbers() {
let n = 0;
while (true)
yield n++;
}
result = takeWhile(x => x < 10, naturalNumbers())
console.log([...result])
HSコードのストレートポートも可能ですが、それはマテリアライズされた配列でのみ機能します(つまり、熱心に):
// would be nice, but JS sucks ;(
// let takeWhile = (f, [x, ...xs]) => f(x) ? [x, ...takeWhile(f, xs)] : [];
let takeWhile = (f, xs) => xs.length ? takeWhileNotEmpty(f, xs) : [];
let takeWhileNotEmpty = (f, [x, ...xs]) => f(x) ? [x, ...takeWhile(f, xs)] : [];
let odd = x => x % 2
a = [1,3,5,7,8,9]
r = takeWhile(odd, a)
console.log(r)
実際、@ naomikがここに示しているように、空のリストを処理するためのより良い方法があります。
let nil = {};
let takeWhile = (f, [x = nil, ...xs]) => (x === nil || !f(x))
? [] : [x, ...takeWhile(f, xs)];
console.log(takeWhile(x => x % 2, [1, 3, 5, 7, 8, 9]));
最後に、最初の試みには意味があります。これは、上記とは異なり、末尾再帰であるため、良いことです。より簡潔に書くことができます
let takeWhile = (f, xs) => take1(f, xs, []);
let take1 = (f, xs, acc) => xs.length ? take2(f, xs, acc) : acc;
let take2 = (f, [x, ...xs], acc) => f(x) ? take1(f, xs, acc.concat(x)) : acc;
両方のアプローチの組み合わせ(つまり、再帰ジェネレーター)は演習として残されました...
この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。
侵害の場合は、連絡してください[email protected]
コメントを追加