31

私は Scala をいじり始めたばかりで、 (命令型オブジェクト指向言語で一般的な、より伝統的な左結合とは対照的に) メソッドを右結合にする方法について学びました。

最初に、Scala でリストのサンプル コードを見たときcons、すべてのサンプルの右側に常にリストがあることに気付きました。

println(1 :: List(2, 3, 4))
newList = 42 :: originalList

::しかし、これを何度も見た後でも、(当時)それが のメソッドであることを知らなかったので、よく考えませんでしたList。私はそれが演算子であると仮定し(これもJavaの演算子の意味で)、その結合性は問題ではありませんでした。Listサンプル コードで が常に右側に表示されるという事実は、偶然の一致のように思えました (単に「好みのスタイル」に過ぎないと思いました)。

これでよくわかりました:::は右結合なので、そのように書かなければなりません。

私の質問は、右結合メソッドを定義できることのポイントは何ですか?

それは純粋に審美的な理由によるものですか、それとも特定の状況で右結合性が実際に左結合性よりも何らかの利点があるのでしょうか?

私の(初心者の)観点からは、どうすればよいかわかりません

1 :: myList

よりも優れています

myList :: 1

しかし、それは明らかに些細な例であるため、公正な比較とは思えません。

4

3 に答える 3

39

手短に言えば、右結合性は、プログラマーが入力する内容とプログラムが実際に実行する内容とを一致させることにより、可読性を向上させることができるというものです。
したがって、「1 :: 2 :: 3」と入力すると、リストがまったく異なる順序で返されるのではなく、List(1, 2, 3) が返されます。
それは、「1 :: 2 :: 3 :: Nil」が実際には

List[Int].3.prepend(2).prepend(1)

scala> 1 :: 2 :: 3:: Nil
res0: List[Int] = List(1, 2, 3)

これは両方です:

  • より読みやすい
  • より効率的 (O(1) に対してprepend、仮想appendメソッドの O(n))

(Reminder, 本からの抜粋Programming in Scala )
メソッドが のような演算子表記法で使用されている場合、メソッド名がコロンで終わらない限り、 —のようa * bに、メソッドは左側のオペランドで呼び出されます。 メソッド名がコロンで終わる場合、メソッドは右側のオペランドで呼び出されます。 したがって、 では、 でメソッドが呼び出され、次のように 1 が渡されます。a.*(b)

1 :: twoThree::twoThreetwoThree.::(1)

List の場合、追加操作の役割を果たします (リストは '1' の後に追加されて ' ' を形成しているように見えます1 2 3が、実際にはリストの前に追加されるのは 1 です)
リストへの追加にかかる時間はリストのサイズに比例して増加するのに対し、 :: を前に追加するには一定の時間がかかるため、クラス List は真の追加操作を提供しません。
myList :: 1myList のコンテンツ全体を '1' に追加しようとしますが、これは myList に 1 を追加するよりも長くなります (' 1 :: myList' のように)

注: ただし、演​​算子の結合性に関係なく、そのオペランドは常に左から右に評価されます。
したがって、 b が不変値への単なる参照ではない式である場合、 a ::: b は次のブロックとしてより正確に扱われます。

{ val x = a; b.:::(x) }

このブロックでは、a が b の前に評価され、この評価の結果がオペランドとして b の ::: メソッドに渡されます。


なぜ左結合と右結合の方法を区別するのですか?

1 :: myListこれにより、実際には右側の式に操作を適用しながら、通常の左結合操作 (' ') の外観を維持できます。

  • それはより効率的です。
  • しかし、逆の結合順 (' 1 :: myList' vs. ' myList.prepend(1)')の方が読みやすいです。

あなたが言うように、私の知る限り、「シンタックスシュガー」。たとえば、 の場合、それらは少し行き過ぎている
可能性があること に注意してください(' ' 右結合演算子と同等です)。foldLeft/:


コメントの一部を含めるには、少し言い換えます。

「追加」関数を左結合と見なす場合は、「 」と記述しますoneTwo append 3 append 4 append 5
ただし、oneTwo に 3、4、および 5 を追加すると (これは書かれている方法で想定されます)、O(N) になります。
「追加」の場合は「::」と同じです。そうではありません。実際には「プリペンド」用です

つまり、' a :: b :: Nil' は ' ' 用List[].b.prepend(a)です

'::' を先頭に追加しても左結合のままである場合、結果のリストは間違った順序になります。
List(1, 2, 3, 4, 5) を返すことを期待しますが、最終的には List(5, 4, 3, 1, 2) を返すことになります。これは、プログラマにとって予期しないことかもしれません。
それは、あなたがしたことは、左結合の順序であったからです:

(1,2).prepend(3).prepend(4).prepend(5) : (5,4,3,1,2)

したがって、右結合により、コードは戻り値の実際の順序と一致します。

于 2009-07-22T03:52:19.993 に答える
3

リストの折りたたみ操作を実行する場合、右結合性と左結合性は重要な役割を果たします。例:

def concat[T](xs: List[T], ys: List[T]): List[T] = (xs foldRight ys)(_::_)

これは完全に正常に機能します。ただし、foldLeft 操作を使用して同じことを実行することはできません

def concat[T](xs: List[T], ys: List[T]): List[T] = (xs foldLeft ys)(_::_)

::右結合だからです。

于 2013-10-28T18:07:56.050 に答える
3

右結合メソッドを定義できることのポイントは何ですか?

右結合メソッドのポイントは、誰かに言語を拡張する機会を与えることだと思います。これは、一般的に演算子オーバーライドのポイントです。

演算子のオーバーロードは便利なので、Scala は次のように述べています。むしろ、なぜ演算子とメソッドを区別するのでしょうか? では、ライブラリの実装者は のような組み込み型とどのように対話するのIntでしょうか? C++ では、彼女はfriend関数をグローバル スコープで使用していたでしょう。すべての型で operator を実装したい場合はどうすればよい::でしょうか?

右結合は、::すべての型に演算子を追加するクリーンな方法を提供します。もちろん、技術的には::演算子は List 型のメソッドです。しかし、少なくとも最後に を無視できれば、これは組み込みの Int および他のすべての型の架空の演算子でもあります。:: Nil

これは、ライブラリーにできるだけ多くのものを実装し、それらをサポートするために言語を柔軟にするという Scala の哲学を反映していると思います。これにより、誰かが SuperList を考え出す機会が得られます。これは、次のように呼び出すことができます。

1 :: 2 :: SuperNil

現在、適切な結合性が末尾のコロンにのみハードコードされているのは少し残念ですが、覚えやすいと思います。

于 2009-08-30T08:12:49.890 に答える