「abstract over」とはどういう意味ですか?


95

Scalaの文献では、「抽象化」というフレーズに出くわすことがよくありますが、意図がわかりません。 たとえば、Martin Oderskyは次のように書いています

メソッド(または「関数」)をパラメーターとして渡すことも、それらを抽象化することもできます。タイプをパラメーターとして指定することも、それらを抽象化することもできます。

別の例として、「Deprecating the Observer Pattern」ペーパーでは、

イベントストリームが一流の値であることの結果は、それらを抽象化できることです。

モナドは「型コンストラクターを抽象化」しているのに対して、私は最初のジェネリックが「型を抽象化する」ことを読みました。また、Cake Patternペーパーにもこのようなフレーズが表示されます。多くのそのような例の1つを引用するには:

抽象型メンバーは具象型のコンポーネントを抽象化する柔軟な方法を提供します。

関連するスタックオーバーフローの質問でさえ、この用語を使用します。 「パラメータ化された型を存在的に抽象化することはできません...」

では、「抽象化」とは実際にはどういう意味ですか?

回答:


124

代数では、日常の概念形成と同様に、抽象化はいくつかの本質的な特性によって物事をグループ化し、それらの他の特定の特性を省略して形成されます。抽象化は、類似点を表す単一の記号または単語の下で統一されます。私たちは違いを抽象化すると言いますが、これは本当に類似性によって統合していることを意味します。

例えば、数字の合計を取るプログラムを考え123

val sumOfOneTwoThree = 1 + 2 + 3

このプログラムはあまり抽象的ではないため、あまり興味深いものではありません。数値のすべてのリストを1つの記号の下に統合することにより、合計している数値を抽象化できますns

def sumOf(ns: List[Int]) = ns.foldLeft(0)(_ + _)

そして、それがリストであることも特に気にしません。リストは特定の型コンストラクター(型を取り、型を返す)ですが、必要な特性(折りたたみ可能)を指定することで、型コンストラクターを抽象化できます。

trait Foldable[F[_]] {
  def foldl[A, B](as: F[A], z: B, f: (B, A) => B): B
}

def sumOf[F[_]](ns: F[Int])(implicit ff: Foldable[F]) =
  ff.foldl(ns, 0, (x: Int, y: Int) => x + y)

また、フォールドできる他のすべての暗黙のFoldableインスタンスを持つことができますList

implicit val listFoldable = new Foldable[List] {
  def foldl[A, B](as: List[A], z: B, f: (B, A) => B) = as.foldLeft(z)(f)
}

val sumOfOneTwoThree = sumOf(List(1,2,3))

さらに、演算とオペランドのタイプの両方を抽象化できます。

trait Monoid[M] {
  def zero: M
  def add(m1: M, m2: M): M
}

trait Foldable[F[_]] {
  def foldl[A, B](as: F[A], z: B, f: (B, A) => B): B
  def foldMap[A, B](as: F[A], f: A => B)(implicit m: Monoid[B]): B =
    foldl(as, m.zero, (b: B, a: A) => m.add(b, f(a)))
}

def mapReduce[F[_], A, B](as: F[A], f: A => B)
                         (implicit ff: Foldable[F], m: Monoid[B]) =
  ff.foldMap(as, f)

今、私たちは非常に一般的なものを持っています。このメソッドは、折りたたみ可能であること、およびモノイドであること、またはモノイドにマッピングできることを証明できる場合は、mapReduceすべて折りたたみます。例えば:F[A]FA

case class Sum(value: Int)
case class Product(value: Int)

implicit val sumMonoid = new Monoid[Sum] {
  def zero = Sum(0)
  def add(a: Sum, b: Sum) = Sum(a.value + b.value)
}

implicit val productMonoid = new Monoid[Product] {
  def zero = Product(1)
  def add(a: Product, b: Product) = Product(a.value * b.value)
}

val sumOf123 = mapReduce(List(1,2,3), Sum)
val productOf456 = mapReduce(List(4,5,6), Product)

モノイドと折りたたみ式を抽象化しました


@coubeatczechコードはREPLで正常に実行されます。使用しているScalaのバージョンと、どのエラーが発生しましたか?
ダニエルC.ソブラル2011年

1
@Apocalisp 2つの最後の例の1つをSet他の折りたたみ可能なタイプにした場合、興味深いでしょう。Stringand連結の例も非常に優れています。
ダニエルC.ソブラル2011年

1
美しい答え、ルナー。ありがとう!ダニエルの提案に従い、mapReduceをまったく変更せずに、暗黙のsetFoldableおよびconcatMonoidを作成しました。私はこれを手探りで進んでいます。
モーガンクレイトン、2011年

6
最後の2行で、SumおよびProductコンパニオンオブジェクトがapply(Int)を定義しているため、ScalaではInt => SumおよびInt => Productとして扱われるという事実を利用するのに少し時間がかかりましたコンパイラ。非常に素晴らしい!
Kris Nuttycombe

素敵な投稿:)!最後の例では、Monoidの暗黙のロジックは不要のようです。これはより簡単です:gist.github.com/cvogt/9716490
cvogt

11

最初の近似では、何かを「抽象化」できるということは、その何かを直接使用する代わりに、そのパラメータを作成するか、そうでなければ「匿名で」使用できることを意味します。

Scalaを使用すると、クラス、メソッド、および値に型パラメーターを、値に抽象(または匿名)型を許可することで、型を抽象化できます。

Scalaでは、メソッドに関数パラメーターを持たせることで、アクションを抽象化できます。

Scalaでは、型を構造的に定義できるようにすることで、機能を抽象化できます。

Scalaでは、高次の型パラメーターを許可することで、型パラメーターを抽象化できます。

Scalaでは、エクストラクターを作成できるようにすることで、データアクセスパターンを抽象化できます。

Scalaでは、暗黙的な変換をパラメーターとして許可することにより、「他のものとして使用できるもの」を抽象化できます。Haskellは型クラスでも同様に機能します。

Scalaでは、(まだ)クラスを抽象化することはできません。クラスを何かに渡し、そのクラスを使用して新しいオブジェクトを作成することはできません。他の言語では、クラスを抽象化できます。

(「モナドアブストラクトタイプコンストラクター」は、非常に限定的な方法でのみ当てはまります。「アハ!私はモナドを理解しました!!」の瞬間が来るまで、それに固執しないでください。)

計算のある側面を抽象化する機能は、基本的にコードの再利用を可能にし、機能のライブラリの作成を可能にします。Scalaは、主流の言語よりも多くの種類の抽象化を可能にし、それに応じてScalaのライブラリーをより強力にすることができます。


1
Manifest、またはを渡し、Classリフレクションを使用してそのクラスの新しいオブジェクトをインスタンス化できます。
ダニエルC.ソブラル2011年

6

抽象化は一種の一般化です。

http://en.wikipedia.org/wiki/Abstraction

Scalaだけでなく、多くの言語では、複雑さを軽減する(または少なくとも情報を理解しやすい部分に分割する階層を作成する)ためのメカニズムが必要です。

クラスは、単純なデータ型を抽象化したものです。基本的なタイプに似ていますが、実際には一般化されています。したがって、クラスは単純なデータ型以上のものですが、多くの共通点があります。

彼が「抽象化する」と言うとき、それはあなたが一般化するプロセスを意味します。したがって、メソッドをパラメータとして抽象化している場合は、それを行うプロセスを一般化しています。たとえば、関数にメソッドを渡す代わりに、それを処理するためのある種の一般化された方法を作成することができます(メソッドをまったく渡さないで、それに対処するための特別なシステムを構築するなど)。

この場合、彼は具体的には問題を抽象化し、問題のような解決策を作成するプロセスを意味します。Cは抽象化する能力がほとんどありません(実行することはできますが、乱雑になり、言語が直接サポートしていません)。C ++で記述した場合は、oopの概念を使用して問題の複雑さを軽減できます(まあ、それは同じ複雑さですが、概念化は一般に簡単です(少なくとも抽象化の観点から考えることを学ぶと))。

たとえば、intのような特別なデータ型が必要であるが、制限されているとしましょう。intのように使用でき、必要なプロパティを持つ新しい型を作成することで、それを抽象化できます。そのようなことをするために私が使用するプロセスは、「抽象化」と呼ばれます。


5

ここに私の狭いショーと解釈を示します。自明で、REPLで実行されます。

class Parameterized[T] { // type as a parameter
  def call(func: (Int) => Int) = func(1)  // function as a parameter
  def use(l: Long) { println(l) } // value as a parameter
}

val p = new Parameterized[String] // pass type String as a parameter
p.call((i:Int) => i + 1) // pass function increment as a parameter
p.use(1L) // pass value 1L as a parameter


abstract class Abstracted { 
  type T // abstract over a type
  def call(i: Int): Int // abstract over a function
  val l: Long // abstract over value
  def use() { println(l) }
}

class Concrete extends Abstracted { 
  type T = String // specialize type as String
  def call(i:Int): Int = i + 1 // specialize function as increment function
  val l = 1L // specialize value as 1L
}

val a: Abstracted = new Concrete
a.call(1)
a.use()

1
コードの "抽象的"なアイデア
user44298

2

他の答えは、どのような抽象化が存在するかについてすでに良い考えを与えています。引用を1つずつ確認して、例を示します。

メソッド(または「関数」)をパラメーターとして渡すことも、それらを抽象化することもできます。タイプをパラメーターとして指定することも、それらを抽象化することもできます。

関数をパラメーターとして渡す:ここではList(1,-2,3).map(math.abs(x))明らかにパラメーターとしてabs渡されます。mapそれ自体は、各リスト要素で特定の特別なことを行う関数を抽象化します。val list = List[String]()タイプパラメータ(文字列)を指定します。代わりに、抽象型メンバーを使用するコレクション型を記述できますval buffer = Buffer{ type Elem=String }。1つの違いは、あなたが書かなければならないということですdef f(lis:List[String])...が、def f(buffer:Buffer)...要素の型は一種第2の方法では「隠された」のであるので、。

イベントストリームが一流の値であることの結果は、それらを抽象化できることです。

Swingでは、イベントが突然「発生」し、ここで今すぐ対処する必要があります。イベントストリームを使用すると、すべての配管をより宣言的な方法で行うことができます。たとえば、Swingで責任のあるリスナーを変更する場合は、古いリスナーを登録解除し、新しいリスナーを登録する必要があります。イベントストリームを使用すると、イベントのソースは単純に渡すことができるものになり、バイトストリームや文字ストリームとそれほど変わらないため、より「抽象的な」概念になります。

抽象型メンバーは、具象型のコンポーネントを抽象化する柔軟な方法を提供します。

上記のBufferクラスはすでにこの例です。


0

上記の回答は優れた説明を提供しますが、1つの文で要約すると、次のようになります。

何かを抽象化することは、無関係なところで無視することと同じです。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.