通常在Scala文献中,我遇到了"抽象结束"这个短语,但我不明白其意图. 例如,马丁奥德斯基写道
您可以将方法(或"函数")作为参数传递,也可以对它们进行抽象.您可以将类型指定为参数,也可以对它们进行抽象.
另一个例子,在"弃用观察者模式"一文中,
我们的事件流是第一类值的结果是我们可以抽象它们.
我已经读过第一阶泛型"抽象类型",而monads"抽象类型构造函数".我们还在Cake Pattern论文中看到了这样的短语.引用许多这样的例子中的一个:
抽象类型成员提供了抽象的具体类型的组件的灵活方式.
即使相关的堆栈溢出问题也使用此术语. "不能存在抽象的参数化类型..."
所以......"抽象"究竟意味着什么?
在代数中,就像在日常概念形成中一样,抽象是通过将事物按某些基本特征分组并省略其特定的其他特征而形成的.抽象统一在表示相似性的单个符号或单词下.我们说我们在差异上抽象,但这实际上意味着我们通过相似性进行整合.
例如,考虑一个程序,它的数字的总和1
,2
和3
:
val sumOfOneTwoThree = 1 + 2 + 3
这个程序不是很有趣,因为它不是很抽象.我们可以通过将所有数字列表集成在一个符号下来抽象我们总结的数字ns
:
def sumOf(ns: List[Int]) = ns.foldLeft(0)(_ + _)
而且我们也不特别关心它是列表.List是一个特定的类型构造函数(接受一个类型并返回一个类型),但是我们可以通过指定我们想要的基本特征(它可以折叠)来抽象类型构造函数:
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]
给定的,我们可以证明它F
是可折叠的,并且A
是monoid或可以映射到一个.例如:
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)
我们已经抽象了 monoids和foldables.
对于第一个近似,能够"抽象"某事意味着不是直接使用那些东西,而是可以创建它的参数,或者"匿名"使用它.
Scala允许您通过允许类,方法和值具有类型参数来抽象类型,并且值具有抽象(或匿名)类型.
Scala允许您通过允许方法具有函数参数来抽象操作.
Scala允许您通过允许在结构上定义类型来抽象功能.
Scala允许您通过允许更高阶类型的参数来抽象类型参数.
Scala允许您通过允许创建提取器来抽象数据访问模式.
通过允许隐式转换作为参数,Scala允许您抽象"可以用作其他东西的东西".Haskell与类型类似.
Scala(还)不允许您对类进行抽象.您不能将类传递给某个东西,然后使用该类创建新对象.其他语言允许抽象类.
("Monads抽象类型构造函数"只是以非常严格的方式才真实.在你拥有"啊哈!我理解monads !!"时刻之前,不要挂断它.)
抽象计算的某些方面的能力基本上是允许代码重用的功能,并且能够创建功能库.Scala允许比更多主流语言抽象出更多种类的东西,而Scala中的库可以相应地更强大.
抽象是一种泛化.
http://en.wikipedia.org/wiki/Abstraction
不仅在Scala中,还有许多语言需要有这样的机制来降低复杂性(或者至少创建一个将信息分成易于理解的层次结构).
类是对简单数据类型的抽象.它有点像基本类型但实际上概括了它们.因此,类不仅仅是一种简单的数据类型,而且与它有许多共同之处.
当他说"抽象"时,他指的是你概括的过程.因此,如果您将方法作为参数进行抽象,那么您可以概括这样做的过程.例如,不是将方法传递给函数,而是可以创建某种类型的通用方法来处理它(例如根本不传递方法,而是构建一个特殊的系统来处理它).
在这种情况下,他特别指的是抽象问题并创建类似问题的解决方案的过程.C几乎没有抽象的能力(你可以做到这一点,但它很快就会变得混乱,而且语言并不直接支持它).如果你用C++编写它,你可以使用oop概念来降低问题的复杂性(嗯,它具有相同的复杂性,但概念化通常更容易(至少一旦你学会从抽象的角度思考)).
例如,如果我需要一个类似于int的特殊数据类型,但是,让我说限制我可以通过创建一个可以像int一样使用但具有我需要的属性的新类型来抽象它.我用来做这种事情的过程将被称为"抽象".
这是我的狭隘表演并讲述解释.这是不言自明的,并在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()