W algebrze, podobnie jak w codziennym tworzeniu pojęć, abstrakcje są tworzone przez grupowanie rzeczy według pewnych zasadniczych cech i pomijanie ich specyficznych innych cech. Abstrakcja jest ujednolicona pod jednym symbolem lub słowem oznaczającym podobieństwa. Mówimy, że abstrahujemy od różnic, ale to naprawdę oznacza, że integrujemy przez podobieństwa.
Rozważmy na przykład program, który bierze sumę liczb 1
, 2
oraz 3
:
val sumOfOneTwoThree = 1 + 2 + 3
Ten program nie jest zbyt interesujący, ponieważ nie jest zbyt abstrakcyjny. Możemy wyabstrahować liczby, które sumujemy, integrując wszystkie listy liczb pod jednym symbolem ns
:
def sumOf(ns: List[Int]) = ns.foldLeft(0)(_ + _)
I nie obchodzi nas też, że jest to lista. Lista jest konstruktorem określonego typu (pobiera typ i zwraca typ), ale możemy abstrahować od konstruktora typu, określając, jaką podstawową cechę chcemy (że można ją złożyć):
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)
Możemy mieć niejawne Foldable
instancje List
i każdą inną rzecz, którą możemy spasować.
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))
Co więcej, możemy abstrahować zarówno od operacji, jak i typu operandów:
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)
Teraz mamy coś całkiem ogólnego. Metoda mapReduce
zwinie wszystkie F[A]
dane, które możemy udowodnić, że F
są składane i A
są monoidalne lub można je zmapować na jeden. Na przykład:
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)
Mamy wydobywane przez monoids i foldables.
Set
lub inny typ składany. Przykład zString
konkatenacją i również byłby całkiem fajny.W pierwszym przybliżeniu, możliwość „abstrahowania” czegoś oznacza, że zamiast używać tego czegoś bezpośrednio, można utworzyć z tego parametr lub w inny sposób używać go „anonimowo”.
Scala umożliwia abstrakcję nad typami, zezwalając klasom, metodom i wartościom na parametry typu, a wartościom na typy abstrakcyjne (lub anonimowe).
Scala pozwala abstrahować od działań, zezwalając metodom na posiadanie parametrów funkcji.
Scala umożliwia abstrakcję funkcji, umożliwiając strukturalne definiowanie typów.
Scala umożliwia abstrahowanie od parametrów typu, zezwalając na parametry typu wyższego rzędu.
Scala umożliwia abstrahowanie od wzorców dostępu do danych, umożliwiając tworzenie ekstraktorów.
Scala pozwala abstrahować od „rzeczy, które mogą być użyte jako coś innego”, zezwalając na niejawne konwersje jako parametry. Haskell postępuje podobnie z klasami typów.
Scala nie pozwala (jeszcze) na abstrakcje nad klasami. Nie możesz przekazać do czegoś klasy, a następnie użyć tej klasy do tworzenia nowych obiektów. Inne języki pozwalają na abstrakcję w stosunku do klas.
(„Monady abstrakcyjne w stosunku do konstruktorów typów” są prawdziwe tylko w bardzo restrykcyjny sposób. Nie rozłączaj się, dopóki nie masz swojego momentu „Aha! Rozumiem monady !!”).
Zdolność do abstrahowania od pewnych aspektów obliczeń jest w zasadzie tym, co umożliwia ponowne użycie kodu i umożliwia tworzenie bibliotek funkcjonalności. Scala pozwala na abstrakcje o wiele więcej rodzajów rzeczy niż bardziej popularne języki, a biblioteki w Scali mogą być odpowiednio potężniejsze.
źródło
Manifest
, a nawet aClass
, i użyć odbicia do utworzenia wystąpienia nowych obiektów tej klasy.Abstrakcja to rodzaj uogólnienia.
http://en.wikipedia.org/wiki/Abstraction
Nie tylko w Scali, ale w wielu językach istnieje potrzeba posiadania takich mechanizmów, aby zmniejszyć złożoność (lub przynajmniej stworzyć hierarchię, która dzieli informacje na łatwiejsze do zrozumienia fragmenty).
Klasa jest abstrakcją w stosunku do prostego typu danych. To trochę jak typ podstawowy, ale w rzeczywistości je uogólnia. Klasa jest więc czymś więcej niż zwykłym typem danych, ale ma z nią wiele wspólnego.
Kiedy mówi „abstrahować”, ma na myśli proces, w którym uogólniasz. Więc jeśli wyabstrahujesz metody jako parametry, uogólniasz proces robienia tego. np. zamiast przekazywania metod do funkcji możesz stworzyć pewien rodzaj uogólnionego sposobu obsługi tego (na przykład nieprzekazywanie metod w ogóle, ale zbudowanie specjalnego systemu do obsługi tego).
W tym przypadku konkretnie ma na myśli proces wyodrębniania problemu i tworzenia rozwiązania problemu podobnego do oop. C ma bardzo małą zdolność do abstrakcji (możesz to zrobić, ale szybko robi się bałagan, a język nie obsługuje tego bezpośrednio). Jeśli napisałeś to w C ++, możesz użyć koncepcji oop, aby zmniejszyć złożoność problemu (cóż, jest to ta sama złożoność, ale konceptualizacja jest ogólnie łatwiejsza (przynajmniej gdy nauczysz się myśleć w kategoriach abstrakcji)).
np., gdybym potrzebował specjalnego typu danych, który byłby podobny do int, ale powiedzmy ograniczony, mógłbym go wyabstrahować, tworząc nowy typ, który mógłby być używany jak int, ale miałby te właściwości, których potrzebowałem. Proces, którego użyłbym do zrobienia czegoś takiego, nazwanoby „abstrakcją”.
źródło
Oto moje wąskie przedstawienie i interpretacja. Jest to oczywiste i działa w REPL.
źródło
Inne odpowiedzi dają już dobre wyobrażenie o tym, jakie rodzaje abstrakcji istnieją. Przejrzyjmy po kolei cytaty i podajmy przykład:
Przekaż funkcję jako parametr: w tym miejscu jest
List(1,-2,3).map(math.abs(x))
wyraźnieabs
przekazywany jako parametr.map
sama abstrahuje od funkcji, która wykonuje określoną rzecz z każdym elementem listy.val list = List[String]()
określa parametr typu (String). Można napisać typ kolekcji, który używa abstrakcyjnych elementów typu zamiast:val buffer = Buffer{ type Elem=String }
. Jedyną różnicą jest to, że musisz pisaćdef f(lis:List[String])...
aledef f(buffer:Buffer)...
, więc typ elementu jest trochę „ukryty” w drugiej metodzie.W Swingu wydarzenie po prostu „dzieje się” nieoczekiwanie i musisz sobie z nim radzić tu i teraz. Strumienie zdarzeń umożliwiają wykonanie wszystkich prac hydraulicznych i okablowania w bardziej deklaratywny sposób. Np. Kiedy chcesz zmienić odpowiedzialnego słuchacza w Swing, musisz wyrejestrować starego i zarejestrować nowego oraz poznać wszystkie krwawe szczegóły (np. Problemy z wątkami). W przypadku strumieni zdarzeń źródło zdarzeń staje się rzeczą, którą można po prostu przekazać, dzięki czemu nie różni się zbytnio od strumienia bajtów lub znaków, stąd koncepcja bardziej „abstrakcyjna”.
Powyższa klasa Buffer jest już tego przykładem.
źródło
Powyższe odpowiedzi stanowią doskonałe wyjaśnienie, ale podsumowując to w jednym zdaniu, powiedziałbym:
źródło