抽象和泛化之间有什么区别?

Aad*_*hah 35 oop abstraction functional-programming nomenclature generalization

我知道抽象是关于采取更具体的东西并使其更抽象.这可能是数据结构或过程.例如:

  1. 数据抽象:矩形是正方形的抽象.它集中在一个正方形有两对相对边的事实上,它忽略了一个正方形的相邻边相等的事实.
  2. 过程抽象:高阶函数map是对过程的抽象,该过程对值列表执行一组操作以产生全新的值列表.它集中在这样一个事实,即程序遍历列表中的每个项目以生成新列表并忽略对列表中每个项目执行的实际操作.

所以我的问题是:抽象与泛化有什么不同?我正在寻找主要与函数式编程相关的答案.但是,如果在面向对象编程中有相似之处,那么我也想了解它们.

fre*_*oma 32

确实是个非常有趣的问题.我在这个主题上发现了这篇文章,该文章简明地指出:

虽然抽象通过隐藏不相关的细节来降低复杂性,但泛化通过用单个构造替换执行类似功能的多个实体来降低复杂性.

让我们来看一个管理图书馆书籍的系统的旧例子.一本书有很多属性(页数,重量,字体大小,封面,......)但是为了我们图书馆的目的,我们可能只需要

Book(title, ISBN, borrowed)
Run Code Online (Sandbox Code Playgroud)

我们刚刚从我们库中的真实书籍中抽象出来,并且只在我们的应用程序的上下文中获取了我们感兴趣的属性.


另一方面,泛化并不试图去除细节,而是使功能适用于更广泛(更通用)的项目范围.通用容器是这种心态一个很好的例子:你不会想要写的实现StringList,IntList等,这就是为什么你宁愿写一个通用的,适用于所有类型(如列表List[T]中斯卡拉).请注意,您没有抽象列表,因为您没有删除任何细节或操作,您只是将它们通常适用于所有类型.

第2轮

@ dtldarek的答案真的是一个非常好的插图!基于此,这里有一些代码可能会进一步澄清.

还记得Book我提到的吗?当然,库中还有其他东西可以借用(我会称之为所有这些对象的集合,Borrowable即使这可能甚至不是一个字:D):

http://f.cl.ly/items/3z0f1S3g1h1m2u3c0l0g/diagram.png

所有这些项目都将在我们的数据库和业务逻辑中具有抽象表示,可能与我们的类似Book.另外,我们可能会定义一个所有Borrowables 共有的特征:

trait Borrowable {
    def itemId:Long
}
Run Code Online (Sandbox Code Playgroud)

然后我们可以编写适用于所有s的通用逻辑Borrowable(此时我们不关心它是一本书还是一本杂志):

object Library {
    def lend(b:Borrowable, c:Customer):Receipt = ...
    [...]
}
Run Code Online (Sandbox Code Playgroud)

总结一下:我们在我们的数据库中存储了所有书籍,杂志和DVD 的抽象表示,因为精确的表示既不可行也不必要.然后我们继续说道

无论是书籍,杂志还是DVD都是客户借用的.它始终是相同的过程.

因此,我们通过定义所有可以借用的东西来概括借用项目的操作Borrowable.

  • 嘿弗雷斯科玛,只是重温旧概念。因此,明确地说,抽象相当于临时多态性,泛化相当于参数多态性。那是对的吗? (2认同)

dtl*_*rek 29

宾语:

门户蛋糕照片

抽象:

在此输入图像描述

概括:

很多甜点

Haskell中的示例:

通过使用具有三个不同接口的优先级队列来实现选择排序:

  • 一个打开的接口,队列被实现为一个排序列表,
  • 一个抽象的接口(所以细节隐藏在抽象层后面),
  • 一个通用接口(细节仍然可见,但实现更灵活).
{-# LANGUAGE RankNTypes #-}

module Main where

import qualified Data.List as List
import qualified Data.Set as Set

{- TYPES: -}

-- PQ new push pop
-- by intention there is no build-in way to tell if the queue is empty
data PriorityQueue q t = PQ (q t) (t -> q t -> q t) (q t -> (t, q t))
-- there is a concrete way for a particular queue, e.g. List.null
type ListPriorityQueue t = PriorityQueue [] t
-- but there is no method in the abstract setting
newtype AbstractPriorityQueue q = APQ (forall t. Ord t => PriorityQueue q t)


{- SOLUTIONS: -}

-- the basic version
list_selection_sort :: ListPriorityQueue t -> [t] -> [t]
list_selection_sort (PQ new push pop) list = List.unfoldr mypop (List.foldr push new list)
  where
    mypop [] = Nothing -- this is possible because we know that the queue is represented by a list
    mypop ls = Just (pop ls)


-- here we abstract the queue, so we need to keep the queue size ourselves
abstract_selection_sort :: Ord t => AbstractPriorityQueue q -> [t] -> [t]
abstract_selection_sort (APQ (PQ new push pop)) list = List.unfoldr mypop (List.foldr mypush (0,new) list)
  where
    mypush t (n, q) = (n+1, push t q)
    mypop (0, q) = Nothing
    mypop (n, q) = let (t, q') = pop q in Just (t, (n-1, q'))


-- here we generalize the first solution to all the queues that allow checking if the queue is empty
class EmptyCheckable q where
  is_empty :: q -> Bool

generalized_selection_sort :: EmptyCheckable (q t) => PriorityQueue q t -> [t] -> [t]
generalized_selection_sort (PQ new push pop) list = List.unfoldr mypop (List.foldr push new list)
  where
    mypop q | is_empty q = Nothing
    mypop q | otherwise  = Just (pop q)


{- EXAMPLES: -}

-- priority queue based on lists
priority_queue_1 :: Ord t => ListPriorityQueue t
priority_queue_1 = PQ [] List.insert (\ls -> (head ls, tail ls))
instance EmptyCheckable [t] where
  is_empty = List.null

-- priority queue based on sets
priority_queue_2 :: Ord t => PriorityQueue Set.Set t
priority_queue_2 = PQ Set.empty Set.insert Set.deleteFindMin
instance EmptyCheckable (Set.Set t) where
  is_empty = Set.null

-- an arbitrary type and a queue specially designed for it
data ABC = A | B | C deriving (Eq, Ord, Show)

-- priority queue based on counting
data PQ3 t = PQ3 Integer Integer Integer
priority_queue_3 :: PriorityQueue PQ3 ABC
priority_queue_3 = PQ new push pop
  where
    new = (PQ3 0 0 0)
    push A (PQ3 a b c) = (PQ3 (a+1) b c)
    push B (PQ3 a b c) = (PQ3 a (b+1) c)
    push C (PQ3 a b c) = (PQ3 a b (c+1))
    pop (PQ3 0 0 0) = undefined
    pop (PQ3 0 0 c) = (C, (PQ3 0 0 (c-1)))
    pop (PQ3 0 b c) = (B, (PQ3 0 (b-1) c))
    pop (PQ3 a b c) = (A, (PQ3 (a-1) b c))

instance EmptyCheckable (PQ3 t) where
  is_empty (PQ3 0 0 0) = True
  is_empty _ = False


{- MAIN: -}

main :: IO ()
main = do
  print $ list_selection_sort priority_queue_1 [2, 3, 1]
  -- print $ list_selection_sort priority_queue_2 [2, 3, 1] -- fail
  -- print $ list_selection_sort priority_queue_3 [B, C, A] -- fail
  print $ abstract_selection_sort (APQ priority_queue_1) [B, C, A] -- APQ hides the queue 
  print $ abstract_selection_sort (APQ priority_queue_2) [B, C, A] -- behind the layer of abstraction
  -- print $ abstract_selection_sort (APQ priority_queue_3) [B, C, A] -- fail
  print $ generalized_selection_sort priority_queue_1 [2, 3, 1]
  print $ generalized_selection_sort priority_queue_2 [B, C, A]
  print $ generalized_selection_sort priority_queue_3 [B, C, A]-- power of generalization

  -- fail
  -- print $ let f q = (list_selection_sort q [2,3,1], list_selection_sort q [B,C,A])
  --         in f priority_queue_1

  -- power of abstraction (rank-n-types actually, but never mind)
  print $ let f q = (abstract_selection_sort q [2,3,1], abstract_selection_sort q [B,C,A]) 
          in f (APQ priority_queue_1)

  -- fail
  -- print $ let f q = (generalized_selection_sort q [2,3,1], generalized_selection_sort q [B,C,A])
  --         in f priority_queue_1
Run Code Online (Sandbox Code Playgroud)

代码也可以通过pastebin获得.

值得注意的是存在主义类型.正如@lukstafi已经指出的那样,抽象类似于存在量词,而泛化类似于通用量词.观察到∀xP(x)暗示∃xP(x)(在非空的宇宙中)之间存在一个非平凡的联系,并且很少有没有抽象的泛化(即使是c ++ - 就像重载函数一样)从某种意义上说是一种抽象).

致谢: 门户蛋糕 Solo的.djttwo的甜点桌.符号基于Portal图稿.

  • +1因为图片可以说千言万语. (7认同)

Ben*_*ith 6

我将用一些例子来描述概括和抽象,而我会参考这个文章。

据我所知,在编程领域中没有抽象和泛化定义的官方资料(我认为Wikipedia可能是您最接近官方定义的文字),因此我改用了我认为的文章。可信的。

概括

该文章指出:

“ OOP中的泛化概念意味着对象封装了对象类别的共同状态和行为。”

因此,例如,如果将概化应用于形状,则所有类型的形状的通用属性是面积和周长。

因此,广义的形状(例如Shape)及其特化的形状(例如Circle)可以按以下类别表示(请注意,该图像取自上述文章)

在此处输入图片说明

同样,如果您在喷气飞机领域工作,则可以将喷气机归纳为一般情况,它具有机翼展度属性。喷气式战斗机的一种特殊化可以是FighterJet,它将继承机翼跨度属性,并拥有战斗机所独有的属性,例如NumberOfMissiles。

抽象化

本文将抽象定义为:

“识别具有系统变异的通用模式的过程;抽象表示通用模式,并提供了一种指定使用哪种变异的方法”(Richard Gabriel)”

在编程领域:

抽象类是允许继承但不能实例化的父类。

因此,在以上“概述”部分给出的示例中,Shape抽象为:

在现实世界中,您永远不会计算通用形状的面积或周长,因为每种形状(例如正方形,圆形,矩形等)都有自己的面积和周长公式,所以您必须知道您拥有哪种几何形状。

但是,除了抽象之外,形状也是一种概括(因为它“封装了对象类别的共同状态和行为”,在这种情况下,对象是形状)。

回到我给出的关于Jets和FighterJets的示例中,Jet不是抽象的,因为Jet的具体实例是可行的,因为它可以存在于现实世界中,与形状不同,即在现实世界中您无法保持形状持有一个形状的实例,例如一个立方体。因此,在飞机示例中,喷气机不是抽象的,而是一种概括,因为可能有喷气机的“具体”实例。