2017-06-10 10 views
1

私は1レベル深い配列の配列を持ち、入れ子配列の長さの合計をと計算する必要があります。つまり、の長さが深いです。
Ramdaでそれを行うには良い慣用方法を理解しようとしています。
私が持っている現在の解決策は、十分に軽い気がしません。おそらく私は何かを逃しているでしょう。
よろしくお願いします。
Ramda長さが深い

const arr = [[1], [2, 3], [4, 5, 6]] 
 

 
const lengthDeep = R.pipe(
 
    R.map(R.prop('length')), 
 
    R.sum 
 
) 
 
console.log(lengthDeep(arr)) // 6
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.24.1/ramda.min.js"></script>

PS:私は毎日のコーディングに適用しようとすることでRAMDAを学んでいます。

+0

実際にデータ構造を '折りたたむ 'ときは' map'を使わないでください。 – ftor

答えて

2

まず、R.prop('length')の代わりにR.lengthを使用できます。あなたはまた、必要な結果がその長さになるまでの配列を、平坦化を検討できます。

R.pipe(R.flatten, R.length) 
+0

それはすばらしく見える!ありがとう。 唯一の欠点は、大きなアレイでのパフォーマンスです。私はそれがあまりにも長さを得るために平らにすることはあまり意味です。 –

1

を、これは非常に簡単かつ簡単な質問ですが、私はそれが救済に興味深いポイントをもたらしますと思います。

これまでに3つの提案があります。私は@ ftorの答えをスキップしています。これはRamdaを学ぶことの一部であるというあなたのコメントを無視しています。しかし、私は折り畳みに関する彼のコメントを含めています。ここで

はソリューションです:

const lengthDeep = R.compose(R.sum, R.map(R.length)); 
const lengthDeep = R.compose(R.length, R.flatten); 
const lengthDeep = R.reduce((total, xs) => total + R.length(xs), 0); 

は(私がcomposepipeから切り替えることに注意してください関数は、単一の行に収まるとき、私は通常composeを使用しますが、私はのように基本的にpipecomposeではないと思います。

これは、問題のさまざまな理解に対応しています。

バージョンAR.compose(R.sum, R.map(R.length)))が最も簡単である、と私はそれが問題の元のプレゼンテーションに最も密接にマップすると信じて:私たちは、「ネストされた配列の長さの合計を」見つけたいです。このバージョンは最も簡単です:それらの長さを見つけてそれらを一緒に追加します。 (これはあなたのバージョンがR.lengthR.prop('length')の代わりになるという@ trincotの見解で強化されたものです)これが私が選択するものです。それは非常に簡単で、それが何をしているのかは明らかです。

バージョンBR.compose(R.length, R.flatten))は、この問題の非常に異なる考え方に対応します。これは、「これらのアレイをすべて1つにまとめると、どのくらいの期間ですか?」という質問に答えることができます。限り、私はそれが唯一の利点は、最も簡単なコードだということがわかります。欠点は、実行に時間がかかることがあり、確かにもっと多くのスペースが必要になることです。

バージョンCR.reduce((total, xs) => total + R.length(xs), 0))はさらに別の概念を含む。このソリューションを説明する最善の方法は、再帰的な説明です。配列の空の配列の深さはゼロです。最初の要素の長さがnである配列の配列の深さの長さは、nに配列の残りの部分の深さを足したものになります。それがあなたの問題の考え方であれば、このバージョンはあなたのためかもしれません。私はテストしていませんが、外側のリストを1回だけループするので、パフォーマンスが向上すると思います。したがって、この機能がコードのボトルネックであることが判明した場合(パフォーマンスの最適化を導入する前にパフォーマンスをテストするのは正しいですか?)、コードは大幅に複雑ですが、その機能に切り替えることができます。 (私は単純なものを見ることはできませんが、これはすでにこれほど十分に読みやすいです)。

また、バージョンAを選択します。重要なことはバージョンCに切り替えるよう促しました。しかし、そうは思われません。


は、このすべては、おそらくの@ ftorさんのコメントに不同意の非常に長いったらしい方法です:「あなたはmapあなた実際にfoldデータ構造を使用しないでください。」私は代わりに、問題の精神モデルに合った最も簡単なコードを使用すべきだと言います。これは、パフォーマンスなどの他の考慮事項によって調整する必要がありますが、デフォルトにする必要があります。この問題の私の考えは、「すべての長さを取って一緒に加える」というモデルに絶対にマッチします。

+0

Hey Scott、私のコメントは意見のみに基づいており、適切な回答に値するものではありません。もちろん、OPは中間配列で自分の折り目を構成することができます。言いたいことは、「最高の」コードが必ずしも最も細いコードではないということです。 – ftor

+1

@ftor、私はあなたのコメントに答えることを考え始めませんでした。私が私の答えの大部分を書いていた時だけ、あなたのコメントと矛盾していることに気付きました。私はそれが全体的に悪い考えではないと確信しています。私はあなたの問題の内訳が好きです。私は、簡潔さはコードの主な美徳ではないことに絶対に同意します。 –

+0

徹底的な説明をありがとうございます。私はこの質問のために多くの活動を期待していませんでした。 –

1

mapReduceという小さなヘルパーを使用して別の方法で行うこともできます。Ramdaのcurryを使用して実装することで、他のRambdaライブラリメンバーのような魔法のカレーインターフェイスを共有できます。

mapReduceは、マッピング関数mと還元関数rを効果的に受け取り、新しい還元関数を作成します。これは、あなたが追加ボーナスとして減速に

を生成したいどこでも使用することができますので、便利な汎用的な機能であり、この解決策は、入力配列を通して反復します

(答えを計算するための最小要件)を1回
// mapReduce :: (a -> b) -> ((c, b) -> c) -> ((c, a) -> c) 
const mapReduce = curry ((m, r) => 
    (x, y) => r (x, m (y))) 

// deepLength :: [[a]] -> Integer 
const deepLength = xs => 
    reduce (mapReduce (length, add), 0, xs) 

// arr :: [[Integer]] 
const arr = [[1], [2, 3], [4, 5, 6]] 

console.log (deepLength (arr)) 
// 6 

mapReduceの多様な有用性を実証するために、私は彼らが少し複雑にしているとき、それは物事を処理することが可能であるかを紹介します - まだ

読み取り可能なプログラムを維持しながら、
// mapReduce :: (a -> b) -> ((c, b) -> c) -> ((c, a) -> c) 
const mapReduce = curry ((m, r) => 
    (x, y) => r (x, m (y))) 

// omap :: (a -> b) -> {k : a} -> {k : b} 
const omap = curry ((f, o) => 
    reduce (mapReduce (k => ({ [k]: f(o[k]) }), Object.assign), {}, keys(o))) 

console.log (omap (add(10), {a: 1, b: 2, c: 3})) 
// {"a": 11, "b": 12, "c": 13} 
+0

マップトランスデューサはここでは適切なツールです。これは基本的に第2引数の関数の構成なので、 'mapReduce'や' mapper'よりも一般的な名前があるのだろうかと思います。 – ftor

+0

アキュムレータがレデューサーの2番目の引数で、右のフォールドを使用してコードを単純化できるかどうか不思議です。最初のものに対して簡単に作成できます。これには、もちろんカレー化された解決策が必要です。私はそれを撃つだろう。 – ftor

+0

@ftor、これはおそらくあなたが描いているものだと思う:[https://repl.it/IgNi/1](https://repl.it/IgNi/1) - はい? – naomik

関連する問題