En álgebra, como en la formación de conceptos cotidianos, las abstracciones se forman agrupando cosas por algunas características esenciales y omitiendo sus otras características específicas. La abstracción se unifica bajo un solo símbolo o palabra que denota las similitudes. Decimos que abstraemos las diferencias, pero esto realmente significa que nos estamos integrando por las similitudes.
Por ejemplo, considere un programa que toma la suma de los números 1
, 2
y 3
:
val sumOfOneTwoThree = 1 + 2 + 3
Este programa no es muy interesante, ya que no es muy abstracto. Podemos abstraer los números que estamos sumando, integrando todas las listas de números bajo un solo símbolo ns
:
def sumOf(ns: List[Int]) = ns.foldLeft(0)(_ + _)
Y tampoco nos importa especialmente que sea una Lista. List es un constructor de tipo específico (toma un tipo y devuelve un tipo), pero podemos abstraer el constructor de tipo especificando qué característica esencial queremos (que se puede plegar):
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)
Y podemos tener Foldable
instancias implícitas para List
y cualquier otra cosa que podamos plegar.
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))
Además, podemos abstraer tanto la operación como el tipo de operandos:
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)
Ahora tenemos algo bastante general. El método mapReduce
doblará cualquier F[A]
dado que podamos probar que F
es plegable y que A
es un monoide o que se puede mapear en uno. Por ejemplo:
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)
Hemos resumido sobre monoides y plegables.
Set
algún otro tipo plegable. Un ejemplo con unaString
y una concatenación también sería genial.En una primera aproximación, ser capaz de "abstraer" algo significa que, en lugar de usar ese algo directamente, puedes convertirlo en un parámetro, o usarlo "anónimamente".
Scala le permite abstraer tipos, permitiendo que las clases, métodos y valores tengan parámetros de tipo y que los valores tengan tipos abstractos (o anónimos).
Scala le permite abstraer acciones, permitiendo que los métodos tengan parámetros de función.
Scala le permite abstraer características, al permitir que los tipos se definan estructuralmente.
Scala le permite abstraer parámetros de tipo, permitiendo parámetros de tipo de orden superior.
Scala le permite abstraer patrones de acceso a datos, permitiéndole crear extractores.
Scala le permite abstraer "cosas que se pueden usar como otra cosa", al permitir conversiones implícitas como parámetros. Haskell hace lo mismo con las clases de tipos.
Scala no le permite (todavía) abstraer clases. No puede pasar una clase a algo y luego usar esa clase para crear nuevos objetos. Otros lenguajes permiten la abstracción sobre clases.
("Mónadas abstractas sobre constructores de tipos" solo es cierto de una manera muy restrictiva. No se obsesione con eso hasta que tenga su momento "¡Ajá! ¡Entiendo las mónadas!").
La capacidad de abstraer algún aspecto de la computación es básicamente lo que permite la reutilización del código y permite la creación de bibliotecas de funcionalidad. Scala permite abstraer muchos más tipos de cosas que los lenguajes más convencionales, y las bibliotecas de Scala pueden ser, en consecuencia, más potentes.
fuente
Manifest
, o incluso aClass
, y usar la reflexión para crear instancias de nuevos objetos de esa clase.Una abstracción es una especie de generalización.
http://en.wikipedia.org/wiki/Abstraction
No solo en Scala, sino en muchos lenguajes, existe la necesidad de tener tales mecanismos para reducir la complejidad (o al menos crear una jerarquía que divida la información en partes más fáciles de entender).
Una clase es una abstracción sobre un tipo de datos simple. Es como un tipo básico, pero en realidad los generaliza. Entonces, una clase es más que un simple tipo de datos, pero tiene muchas cosas en común.
Cuando dice "abstraer" se refiere al proceso mediante el cual se generaliza. Entonces, si está abstrayendo métodos como parámetros, está generalizando el proceso de hacerlo. por ejemplo, en lugar de pasar métodos a funciones, podría crear algún tipo de forma generalizada de manejarlo (como no pasar métodos en absoluto, pero construir un sistema especial para manejarlo).
En este caso, se refiere específicamente al proceso de abstraer un problema y crear una solución similar a la del problema. C tiene muy poca capacidad para abstraer (puedes hacerlo, pero se complica muy rápido y el lenguaje no lo admite directamente). Si lo escribió en C ++, podría usar conceptos de oop para reducir la complejidad del problema (bueno, es la misma complejidad pero la conceptualización es generalmente más fácil (al menos una vez que aprende a pensar en términos de abstracciones)).
por ejemplo, si necesitaba un tipo de datos especial que fuera como un int pero, digamos restringido, podría abstraerlo creando un nuevo tipo que podría usarse como un int pero que tuviera las propiedades que necesitaba. El proceso que usaría para hacer tal cosa se llamaría "abstracción".
fuente
Aquí está mi interpretación limitada de show and tell. Se explica por sí mismo y se ejecuta en REPL.
fuente
Las otras respuestas ya dan una buena idea de qué tipo de abstracciones existen. Repasemos las citas una por una y proporcionemos un ejemplo:
Pasar función como parámetro:
List(1,-2,3).map(math.abs(x))
Claramente aquíabs
se pasa como parámetro.map
en sí mismo se abstrae de una función que hace algo especial con cada elemento de la lista.val list = List[String]()
especifica un parámetro de tipo (String). Se podría escribir un tipo de colección que utiliza miembros de tipo abstracto en su lugar:val buffer = Buffer{ type Elem=String }
. Una diferencia es que tienes que escribirdef f(lis:List[String])...
perodef f(buffer:Buffer)...
, por lo que el tipo de elemento está "oculto" en el segundo método.En Swing, un evento simplemente "ocurre" de la nada, y tienes que lidiar con él aquí y ahora. Los flujos de eventos le permiten realizar toda la instalación y el cableado de una manera más declarativa. Por ejemplo, cuando desee cambiar el oyente responsable en Swing, debe anular el registro del antiguo y registrar el nuevo, y conocer todos los detalles sangrientos (por ejemplo, problemas de subprocesos). Con los flujos de eventos, la fuente de los eventos se convierte en algo que puede simplemente pasar, por lo que no es muy diferente de un flujo de bytes o caracteres, por lo tanto, un concepto más "abstracto".
La clase Buffer anterior ya es un ejemplo de esto.
fuente
Las respuestas anteriores brindan una excelente explicación, pero para resumirla en una sola oración, diría:
fuente