「zip」がコレクションのぶら下がり尾を無視するのはなぜですか?


12

C#、Scala、Haskell、Lisp、およびPythonのzip動作は同じです。1つのコレクションがより長い場合、テールは黙って無視されます。

例外もスローされる可能性がありますが、このアプローチを使用している言語は聞いていません。

これは私を困惑させます。誰かがzipそのように設計されている理由を知っていますか?新しい言語の場合は、他の言語がこの方法で行うため、完了します。しかし、根本的な理由は何でしたか?

ここで、事実に基づいた、歴史に基づいた質問をしている。誰かがそれを好むのか、それとも良いアプローチか悪いアプローチなのかではない。

更新:どうすればよいかと聞かれたら、例外をスローします。配列をインデックス化するのとほとんど同じです(「古い」言語はあらゆる種類のマジックを行いましたが、境界外のインデックス、UB、配列を展開する方法、等)。


10
ファンクターが持っているテールを無視しなかった場合、無限シーケンスを使用するのはより面倒です。特に、非無限範囲の長さを取得するのに費用がかかる/複雑である/不可能な場合。
デュプリケータ

2
これは予想外で奇妙だと思われるようです。私はそれが明らかであり、実際、避けられないものだと思います。等しくない長さのコレクションをzip圧縮するときに何をしたいですか?
キリアンフォス

@KilianFoth、例外がスローされます。
greenoldman

@Deduplicator、いいね。サイレントテールドロップを使用するとzipWithIndex、自然数ジェネレーターを提供して自然に表現できます。さて、唯一の情報が欠けています- それは何の理由ですか?:-)(ところで、回答としてコメントを再投稿してください、ありがとう)。
greenoldman

1
Pythonにはitertools.izip_longestがあり、効果的にNoneで入力を自動パッドします。実際にzipを使用する場合、zipよりも頻繁に選択します。私はもう選択の背後にある理由を思い出すことはできません。Pythonにはすでに@greenoldmanの場合のenumerate()があり、これはよく使用します。
スターウィーバー

回答:


11

それはほとんど常にあなたが望むものであり、そうでない場合は、自分で埋めることができます。

主な問題は、最初にを起動したときの長さがわからないレイジーセマンティクスzipであるため、開始時に例外をスローすることはできません。最初にすべての共通要素を返す必要があり、次に例外をスローしますが、これはあまり役に立ちません。

それはスタイルの問題でもあります。命令型プログラマは、至る所で境界条件を手動でチェックすることに慣れています。機能的なプログラマーは、設計で失敗することのない構造を好みます。例外は非常にまれです。関数が妥当なデフォルトを返す方法があれば、関数型プログラマーがそれを採用します。構成可能性が重要です。


私にできることではなく、歴史的な理由について尋ねています。2番目の段落-間違いzipです。現在の実装方法を見てください。例外のスローとは、単に「歩留まりの停止」を「スロー」に変更することです。3番目の段落-境界外に到達するために空の要素を返すことは失敗しませんが、FP開発者がそれが良いデザインであると投票することはないでしょう。
greenoldman

3
2番目の段落は、すべての実装に適用されるわけではなく、本当に怠zyな実装にのみ適用されます。あなたの場合はzip2つの無限のシーケンスは、開始時にサイズがわかりません。3番目の段落で、妥当なデフォルトを述べました。この場合、空を返すことは合理的ではありませんが、テールをドロップすることは明らかです。
カールビーレフェルト

ああ、最後にあなたのポイントを見る-怠zyな言語で例外をスローすることは技術的な置き換えではなく、振る舞いの完全な変更です。開始時に例外をスローする必要があるため、都合の良いときにいつでもテールを無視することができます
greenoldman

3
+1これはすばらしい答えです。「機能プログラマは設計で失敗することのない構造を好む」ので、機能プログラマが行う設計決定の大部分の背後にある最大の動機は雄弁に述べています。命令型プログラマーは、「聞かないで」と言うルールを持っています。FPは、最終段階まで結果チェックを必要とせずに、命令を継続的に伝えることに焦点を当てることにより、これを第N程度にします。Composabilityが王様だから、失敗することはありません。非常によく言いました。
ジミーホファ

12

テールを完成させる明確な方法がないためです。どのようにそれを行うかの選択は、非自明な尾になります。

秘Theは、最短リストを明示的に長くして、最長の長さと期待値を一致させることです。

zipがそれを行ってくれた場合、どの値が直感的に入力されているのかわかりません。リストを循環させましたか?それは空の値を繰り返しましたか?あなたのタイプの空値とは何ですか?

尾が長くなる方法を直感するために使用できるzipの動作に影響はないため、消費者が期待しないかもしれない値を作成するのではなく、利用可能な値を操作することが合理的な唯一の方法です。


また、特定のよく知られたセマンティクスを持つ非常に特定のよく知られた関数を参照していることを忘れないでください。しかし、それはあなたが類似しているがわずかに異なる機能を作成できないことを意味しません。ない一般的な機能がありますという理由だけでx、あなたはあなたがしたいあなたの特定の目的のために決めることができないという意味ではありませんxy

これと他の多くの一般的なFPスタイル関数が一般的である理由を覚えていますが、それらはシンプルで一般化されているため、コードを微調整して使用し、必要な動作を得ることができます。たとえば、C#では次のことができます。

IEnumerable<Tuple<T, U>> ZipDefaults(IEnumerable<T> first, IEnumerable<U> second)
{
    return first.Count() < second.Count()
        ? first.Concat(Enumerable.Repeat(default(T), second.Count() - first.Count())).Zip(second)
        : first.Zip(second.Concat(Enumerable.Repeat(default(U), first.Count() - second.count())))
}

または他の単純なもの。FPアプローチは、上記のように実装を非常に小さくするだけでなく、ピースを再利用できるため、変更を非常に簡単にします。


わかりましたが、コレクションに他の何かと一致させるために強制するときだけです-コレクション(配列)のインデックス付けと比較してください。範囲外のインデックスがある場合、展開して配列する必要があると考え始めることができますか?または、静かにリクエストを無視することもできます。しかし、しばらくの間、例外をスローするという一般的な概念があります。ここでも同じです。一致するコレクションがない場合は、例外をスローします。なぜこのアプローチがとられなかったのですか?
greenoldman

2
zip多くの場合、直感的なソリューションであるヌルを埋めることができます。タイプを考慮してくださいzip :: [a] -> [b] -> [(Maybe a, Maybe b)]。確かに、結果の型はbit ^ H ^ Hとは非常に非現実的ですが、その上に他の動作(ショートカット、例外)を簡単に実装できます。
アモン

1
@amon:それはまったく直観的ではなく、ばかげています。すべての引数をnullチェックするだけです。
DeadMG

4
@amonすべての型がnullを持っているわけではありません、それは私が意図したことですmempty、オブジェクトはスペースを埋めるためにnullを持っていますが、intや他の型についてもそのようなことを考えなければなりませんか?確かに、C#にはdefault(T)すべての言語があるわけではありませんが、C#の場合でも、それは本当に明白な動作ですか?私はそうは思わない
ジミー・ホッファ

1
@amon長いリストの消費されていない部分を返す方がおそらく便利でしょう。必要に応じて、それらを使用して事後の長さが等しいかどうかを確認できます。また、リストを再度走査することなく、未圧縮のテールで再圧縮または何かを行うことができます。
ドーバル
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.