36

パラメーターなしのメソッドとパラメーターなしのメソッドの違いは理解していますが、パラメーターなしのメソッドを必要とした言語設計の選択についてはよくわかりません。

私が考えることができる欠点:

  • ややこしい。毎週、または 2 週間に 1 回、ここまたは Scala メーリング リストで質問があります。
  • それは複雑です; また、 と を区別する必要が() => Xあり=> Xます。
  • それはあいまいです:x.toFoo(y)それが言うことを意味しますか、またはx.toFoo.apply(y)? (答え:のメソッドのオーバーロードとxのメソッドのオーバーロードによって異なりますが、衝突が発生した場合は、それを呼び出そうとするまでエラーは表示されません。)toFooFooapply
  • 演算子スタイルのメソッド呼び出し構文を台無しにします。メソッドをチェーンするとき、またはセミコロンの干渉を避けるために最後に、引数の代わりに使用するシンボルがありません。ゼロ引数メソッドでは、空のパラメーター リストを使用できます()

現在、クラスで両方を定義することはできません。メソッドが既に定義されているというエラーが表示されます。また、どちらも に変換されFunction0ます。

メソッドdef foodef foo()まったく同じものを作成して、括弧の有無にかかわらず呼び出すことができるようにしないのはなぜですか? その方法の利点は何ですか?

4

5 に答える 5

31

カリー化、それが理由

ダニエルは、パラメーターなしのメソッドが必要な理由を説明するのに非常に役立ちました。それらがゼロパラメータメソッドと区別される理由を説明します。

多くの人は、パラメーターのない関数とパラメーターのない関数の違いを、あいまいな形式の構文糖衣と見なしています。実のところ、これは純粋に Scala がカリー化をどのようにサポートしているかの成果物です (完全を期すために、カリー化とは何か、そしてなぜ私たちがカリー化を好むのかについてのより詳細な説明については、以下を参照してください)。

形式的には、関数は、0 個以上のパラメータ リストを持つことができ、それぞれに 0 個以上のパラメータがあります。
これは、以下が有効であることを意味します: def adef b()だけでなく、不自然def c()()def d(x: Int)()()(y: Int)なども...

関数def foo = ???にパラメーター リストがありません。関数def bar() = ???には、パラメーター リストが 1 つだけあり、パラメーターはありません。2 つの形式を混同する追加のルールを導入すると、一貫した言語機能としてのカリー化が弱体化def aする可能性がdef b()ありますdef c()()def d(x: Int)()()(y: Int)と同等def e()(x: Int)(y: Int)()()です。

カリー化が無関係なケースの 1 つは、Java 相互運用を扱う場合です。Java はカリー化をサポートしていないため、"test".length()(を直接呼び出すjava.lang.String#length()) のようなゼロ パラメータ メソッドを としても呼び出すための構文糖衣を導入しても問題はありません"test".length

カリー化の簡単な説明

Scala は、数学者 Haskell Curry にちなんで名付けられた「currying」と呼ばれる言語機能をサポートしています。
カリー化により、いくつかのパラメーター リストを使用して関数を定義できます。たとえば、次のようになります。

def add(a: Int)(b: Int): Int = a + b
add(2)(3) // 5

incの部分適用に関して定義できるようになったため、これは便利ですadd

def inc: Int => Int = add(1)
inc(2) // 3

カリー化は、ライブラリを介して制御構造を導入する方法として最もよく見られます。たとえば、次のようになります。

def repeat(n: Int)(thunk: => Any): Unit = (1 to n) foreach { _ => thunk }

repeat(2) {
  println("Hello, world")
}

// Hello, world
// Hello, world

repeat要約すると、カリー化を使用する別の機会がどのように開かれるかを確認してください。

def twice: (=> Any) => Unit = repeat(2)

twice {
  println("Hello, world")
}

// ... you get the picture :-)
于 2013-04-26T20:35:41.593 に答える
13

ML で定期的に発生する問題の良い点の 1 つは、定期的に回答があることです。

「私たちの何が悪いの?」というスレッドに誰が抵抗できますか?

https://groups.google.com/forum/#!topic/scala-debate/h2Rej7LlB2A

差出人: マーティン・オデルスキー日付: 2012 年 3 月 2 日金曜日、午後 12 時 13 分件名: Re: [scala-debate] 私たちの何が問題なのか...

Java のイディオムを Scala でスムーズに動作させるために、私たちが後ろ向きに曲がろうとしているというのは、「私たちのやり方が間違っている」と考える人がいます。原則として、def length() と def length は異なります。申し訳ありませんが、String は Java クラスであるため、s.length ではなく s.length() と記述する必要があります。私たちは、s.length から s.length() への自動変換を認めることによって、それを紙に書き留めようと懸命に取り組んでいます。それはそのままで問題です。型システムで2つが識別されるようにそれを一般化することは、確実に破滅的な方法です。次に、どのように曖昧さを解消しますか:

type Action = () => () def foo: アクション

では、foo は Action 型か () 型か? foo() はどうですか?

マーティン

そのスレッドからの私のお気に入りのポールプ フィクション:

On Fri, Mar 2, 2012 at 10:15 AM, Rex Kerr <ich...@gmail.com> wrote:

>This would leave you unable to distinguish between the two with 
>structural types, but how often is the case when you desperately 
>want to distinguish the two compared to the case where distinguishing 
>between the two is a hassle?


/** Note to maintenance programmer: It is important that this method be
 *  callable by classes which have a 'def foo(): Int' but not by classes which
 *  merely have a 'def foo: Int'.  The correctness of this application depends
 *  on maintaining this distinction.
 *  
 *  Additional note to maintenance programmer: I have moved to zambia.
 *  There is no forwarding address.  You will never find me.
 */
def actOnFoo(...)

したがって、この機能の根底にある動機は、この種の ML スレッドを生成することです。

グーグルロジーのもう1つのビット:

2010 年 4 月 1 日木曜日の午後 8 時 4 分に、Rex Kerr <[隠しメール]> は次のように書いています。

「def getName: String」は「def getName(): String」と同じだと思います

いいえ、そうではありません。どちらもパラメーターなしでメソッドを呼び出しますが、一方は「パラメーター リストがゼロのメソッド」であり、もう一方は「空のパラメーター リストが 1 つあるメソッド」です。さらに困惑したい場合は、 def getName()(): String を試してください (そして、その署名でクラスを作成してください)!

Scala はパラメーターを単なるリストではなく、リストのリストとして表現します。

リスト() != リスト(リスト())

特に2つの違いがほとんどないため、そして両方が自動的に関数シグネチャ () => String に変換される可能性があるため、これは一種の風変わりな迷惑です。

真実。実際、パラメーターのないメソッドとパラメーター リストが空のメソッドとの混同は、完全に Java 相互運用性によるものです。それらは異なるはずですが、そうするとJavaメソッドを扱うのは面倒です。文字列の長さを取得するたびに str.length() を書かなければならないことを想像できますか?

乾杯

于 2012-09-09T15:16:45.433 に答える
10

まず、パラメーターなしのメソッド() => X=> Xはまったく関係ありません。

さて、次のようなものを書くのはかなりばかげているように見えます:

var x() = 5
val y() = 2
x() = x() + y()

ここで、上記のパラメーターなしのメソッドとの関係に従わない場合は、均一アクセスの原則を調べる必要があります。上記はすべてメソッド宣言であり、それらはすべて で置き換えることができますdef。つまり、括弧を削除すると仮定します。

于 2012-09-09T00:46:26.177 に答える
8

言及された慣例の事実 (副作用と非副作用) に加えて、いくつかのケースで役立ちます。

空括弧を持つことの有用性

// short apply syntax

object A {
  def apply() = 33
}

object B {
  def apply   = 33
}

A()   // works
B()   // does not work

// using in place of a curried function

object C {
  def m()() = ()
}

val f: () => () => Unit = C.m

括弧がないことの有用性

// val <=> def, var <=> two related defs

trait T { def a:   Int; def a_=(v: Int): Unit }
trait U { def a(): Int; def a_=(v: Int): Unit }

def tt(t: T): Unit = t.a += 1  // works
def tu(u: U): Unit = u.a += 1  // does not work

// avoiding clutter with apply the other way round

object D {
  def a   = Vector(1, 2, 3)
  def b() = Vector(1, 2, 3)
}

D.a(0)  // works
D.b(0)  // does not work

// object can stand for no-paren method

trait E
trait F { def f:   E }
trait G { def f(): E }

object H extends F {
  object f extends E  // works
}

object I extends G {
  object f extends E  // does not work
}

したがって、言語の規則性の観点から、区別することは理にかなっています (特に最後に示したケースの場合)。

于 2012-09-09T09:29:51.633 に答える
4

パラメータなしのメソッドで変更可能な状態にアクセスできるため、両方が可能であると言えます。

class X(private var x: Int) {
  def inc() { x += 1 }
  def value = x
}

このメソッドvalueには副作用はありません (変更可能な状態にのみアクセスします)。この動作については、 Programming in Scalaで明示的に言及されています。

このようなパラメーターなしのメソッドは、Scala では非常に一般的です。対照的に、def height(): Int などの空の括弧で定義されたメソッドは、空括弧メソッドと呼ばれます。推奨される規則は、パラメーターがなく、メソッドが包含オブジェクトのフィールドを読み取ることによってのみ可変状態にアクセスする場合は常にパラメーターなしのメソッドを使用することです (特に、可変状態を変更しません)。

この規則は、均一アクセスの原則をサポートしています [...]

要約すると、Scala のスタイルでは、パラメーターをとらず、パラメーターなしのメソッドとして副作用のないメソッドを定義することが推奨されます。つまり、空の括弧を省略します。一方、括弧なしで副作用を持つメソッドを定義するべきではありません。そのメソッドの呼び出しがフィールド選択のように見えるからです。

于 2012-09-08T22:45:36.297 に答える