Na álgebra, como na formação de conceitos cotidianos, as abstrações são formadas pelo agrupamento de coisas por algumas características essenciais e omissão de suas outras características específicas. A abstração é unificada sob um único símbolo ou palavra denotando as semelhanças. Dizemos que abstraímos as diferenças, mas isso realmente significa que estamos nos integrando pelas semelhanças.
Por exemplo, considere um programa que leva a soma dos números 1
, 2
e 3
:
val sumOfOneTwoThree = 1 + 2 + 3
Este programa não é muito interessante, pois não é muito abstrato. Podemos abstrair os números que estamos somando, integrando todas as listas de números em um único símbolo ns
:
def sumOf(ns: List[Int]) = ns.foldLeft(0)(_ + _)
E não nos importamos particularmente se é uma lista também. List é um construtor de tipo específico (pega um tipo e retorna um tipo), mas podemos abstrair o construtor de tipo especificando qual característica essencial queremos (que pode ser dobrada):
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)
E podemos ter Foldable
instâncias implícitas para List
qualquer outra coisa que possamos dobrar.
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))
Além do mais, podemos abstrair a operação e o 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)
Agora temos algo bastante geral. O método mapReduce
dobrará qualquer F[A]
dado que possamos provar que F
é dobrável e que A
é um monóide ou pode ser mapeado em um. Por exemplo:
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)
Temos abstraída sobre monoids e foldables.
Set
ou algum outro tipo dobrável. Um exemplo com aString
concatenação e também seria muito legal.Para uma primeira aproximação, ser capaz de "abstrair" algo significa que em vez de usar esse algo diretamente, você pode fazer um parâmetro dele, ou então usá-lo "anonimamente".
Scala permite que você abstraia tipos, permitindo que classes, métodos e valores tenham parâmetros de tipo, e os valores tenham tipos abstratos (ou anônimos).
Scala permite que você abstraia ações, permitindo que os métodos tenham parâmetros de função.
Scala permite que você abstraia recursos, permitindo que os tipos sejam definidos estruturalmente.
Scala permite abstrair parâmetros de tipo, permitindo parâmetros de tipo de ordem superior.
Scala permite que você abstraia os padrões de acesso a dados, permitindo a criação de extratores.
Scala permite abstrair "coisas que podem ser usadas como outra coisa", permitindo conversões implícitas como parâmetros. Haskell faz o mesmo com classes de tipo.
Scala (ainda) não permite que você abstraia as classes. Você não pode passar uma classe para algo e, em seguida, usar essa classe para criar novos objetos. Outras linguagens permitem abstração sobre classes.
("Mônadas abstraem sobre construtores de tipo" só é verdadeiro de uma forma muito restritiva. Não se preocupe com isso até que você tenha seu momento "Aha! Eu entendo mônadas !!".)
A capacidade de abstrair algum aspecto da computação é basicamente o que permite a reutilização de código e permite a criação de bibliotecas de funcionalidade. O Scala permite que muito mais tipos de coisas sejam abstraídas do que as linguagens convencionais, e as bibliotecas no Scala podem ser correspondentemente mais poderosas.
fonte
Manifest
, ou mesmo umClass
, e usar reflexão para instanciar novos objetos dessa classe.Uma abstração é uma espécie de generalização.
http://en.wikipedia.org/wiki/Abstraction
Não apenas no Scala, mas em muitas linguagens, é necessário ter tais mecanismos para reduzir a complexidade (ou pelo menos criar uma hierarquia que particione as informações em partes mais fáceis de entender).
Uma classe é uma abstração sobre um tipo de dados simples. É como um tipo básico, mas na verdade os generaliza. Portanto, uma classe é mais do que um tipo de dados simples, mas tem muitas coisas em comum com ela.
Quando ele diz "abstraindo", ele se refere ao processo pelo qual você generaliza. Portanto, se você abstrai os métodos como parâmetros, está generalizando o processo de fazer isso. por exemplo, em vez de passar métodos para funções, você pode criar algum tipo de maneira generalizada de lidar com isso (como não passar métodos, mas construir um sistema especial para lidar com isso).
Nesse caso, ele se refere especificamente ao processo de abstrair um problema e criar uma solução semelhante a oop para o problema. C tem muito pouca habilidade de abstrair (você pode fazer isso, mas fica confuso muito rápido e a linguagem não oferece suporte direto). Se você escreveu em C ++, você poderia usar conceitos oop para reduzir a complexidade do problema (bem, é a mesma complexidade, mas a conceituação é geralmente mais fácil (pelo menos depois que você aprender a pensar em termos de abstrações)).
por exemplo, se eu precisasse de um tipo de dado especial que fosse como um int, mas, digamos, restrito, eu poderia abstrair sobre ele criando um novo tipo que poderia ser usado como um int, mas tinha as propriedades de que eu precisava. O processo que eu usaria para fazer isso seria chamado de "abstração".
fonte
Aqui está o meu estreito show and tell interpretação. É autoexplicativo e roda no REPL.
fonte
As outras respostas já dão uma boa ideia dos tipos de abstrações existentes. Vamos repassar as citações uma por uma e dar um exemplo:
Passe a função como um parâmetro:
List(1,-2,3).map(math.abs(x))
Claramenteabs
é passado como parâmetro aqui.map
se abstrai sobre uma função que faz uma certa coisa especial com cada elemento da lista.val list = List[String]()
especifica um parâmetro de tipo (String). Você poderia escrever um tipo de coleção que utiliza membros de tipo abstrato em vez disso:val buffer = Buffer{ type Elem=String }
. Uma diferença é que você tem que escreverdef f(lis:List[String])...
masdef f(buffer:Buffer)...
, então o tipo de elemento fica meio "escondido" no segundo método.No Swing, um evento simplesmente "acontece" do nada, e você tem que lidar com isso aqui e agora. Os fluxos de eventos permitem que você faça todo o encanamento e a fiação de uma forma mais declarativa. Por exemplo, quando você deseja alterar o ouvinte responsável no Swing, você deve cancelar o registro do antigo e registrar o novo, e saber todos os detalhes sangrentos (por exemplo, problemas de threading). Com os fluxos de eventos, a origem dos eventos torna-se algo que você simplesmente pode repassar, tornando-o não muito diferente de um fluxo de bytes ou caracteres, portanto, um conceito mais "abstrato".
A classe Buffer acima já é um exemplo disso.
fonte
As respostas acima fornecem uma explicação excelente, mas para resumir em uma única frase, eu diria:
fonte