抽象和泛化之间有什么区别? [英] What's the difference between abstraction and generalization?

查看:257
本文介绍了抽象和泛化之间有什么区别?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

据我所知,抽象是关于更具体的东西,使其更抽象。这可能是数据结构或过程。例如:


  1. 数据抽象:矩形是正方形的抽象。它集中于一个正方形具有两对相对侧的事实,并且忽略了正方形的相邻侧相等这一事实。 order函数 map 是一个过程的抽象,它对值列表执行一些操作以生成全新的值列表。它着重于这样一个事实,即该过程遍历列表中的每个项目以生成新列表并忽略在列表中的每个项目上执行的实际操作。

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





抽象:



概括:



Haskell示例

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


  • 一个开放的接口,队列被实现为一个有序列表,
  • 一个抽象接口(所以细节被隐藏在层抽象),
  • 一个通用接口(细节仍然可见,但实施更加灵活)。

$ b

  { - # LANGUAGE RankNTypes# - } 

模块Main其中

导入限定的Data.List作为列表
导入限定的Data.Set作为集合

{ - 类型: - }

- PQ新推弹出
- 意图不存在确定队列是否为空的构建方式
data PriorityQueue qt = PQ(qt)(t - > q t - > q t)(q t - >(t,q t))
- 对于特定的队列有一个具体的方法,例如, List.null
类型ListPriorityQueue t = PriorityQueue [] t
- 但抽象设置中没有方法
newtype AbstractPriorityQueue q = APQ(forall t。Ord t => PriorityQueue qt )


{ - 解决方案: - }

- 基本版本
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 - 这是可能的,因为我们知道队列由一个列表表示
mypop ls = Just(pop ls)


- 这里我们抽象出队列,所以我们需要保持队列的大小为
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)
其中
mypush t(n,q )=(n + 1,push tq)
mypop(0,q)= Nothing
mypop(n,q)= let(t,q')= -1,q'))


- 这里我们将第一种解决方案概括为允许检查队列是否为空的所有队列
class EmptyCheckable q其中
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 |否则= Just(pop q)


{ - 示例: - }

- 基于列表的优先级队列
priority_queue_1 :: Ord t = > ListPriorityQueue t
priority_queue_1 = PQ [] List.insert(\ ls - >(head ls,tail ls))
实例EmptyCheckable [t]其中
is_empty = List.null

- 基于集合的优先级队列
priority_queue_2 :: Ord t => PriorityQueue Set.Set t
priority_queue_2 = PQ Set.empty Set.insert Set.deleteFindMin
instance EmptyCheckable(Set.Set t)其中
is_empty = Set.null

- 专门为它设计的任意类型和队列
data ABC = A | B |基于计数
数据的优先级队列PQ3 t = PQ3整数整数整数
priority_queue_3 :: PriorityQueue PQ3 ABC
(Eq,Ord, priority_queue_3 = PQ new push pop
其中
new =(PQ3 0 0 0)
push A(PQ3 abc)=(PQ3(a + 1)bc)
push B PQ3 abc)=(PQ3 a(b + 1)c)
push C(PQ3 abc)=(PQ3 ab(c + 1))
pop(PQ3 0 0 0)= undefined $ b $ (PQ3 0 0 c)=(C,(PQ3 0 0(c-1)))
pop(PQ3 0 bc)=(B,(PQ3 0(b-1)c))
$ pop(PQ3 abc)=(A,(PQ3(a-1)bc))

实例EmptyCheckable(PQ3 t)其中
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] - 失败
打印$ abstract_selection_sort(APQ pri org_queue_1)[B,C,A] - APQ隐藏队列
print $ abstract_selection_sort(APQ priority_queue_2)[B,C,A] - 抽象层后面
- print $ abstract_selection_sort APQ priority_queue_3)[B,C,A] - 失败
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] - 泛化的力量

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

- 抽象的力量(rank-n-types实际上,但没关系)
print $ let fq =( abstract_selection_sort q [2,3,1],abstract_selection_sort q [B,C,A])
in f(APQ priority_queue_1)

- 失败
- print $ let fq =(generalized_selection_sort q [2,3,1],generalized_selection_sort q [B,C,A])
- in f priority_q ueue_1

代码也可以通过 pastebin



值得注意的是存在类型。正如@lukstafi所指出的那样,抽象与存在量词类似,泛化类似于通用量词。
观察到∀xP(x)意味着xP(x)(在非空宇宙中)这一事实与不存在抽象的泛化之间存在一个不重要的联系(即使是c ++ - 像重载函数在某种意义上构成了一种抽象)。



学分: Solo
甜品桌由 djttwo
该符号基于门户网站图稿。


I understand that abstraction is about taking something more concrete and making it more abstract. That something may be either a data structure or a procedure. For example:

  1. Data abstraction: A rectangle is an abstraction of a square. It concentrates on the fact a square has two pairs of opposite sides and it ignores the fact that adjacent sides of a square are equal.
  2. Procedural abstraction: The higher order function map is an abstraction of a procedure which performs some set of operations on a list of values to produce an entirely new list of values. It concentrates on the fact that the procedure loops through every item of the list in order to produce a new list and ignores the actual operations performed on every item of the list.

So my question is this: how is abstraction any different from generalization? I'm looking for answers primarily related to functional programming. However if there are parallels in object-oriented programming then I would like to learn about those as well.

解决方案

Object:

Abstraction:

Generalization:

Example in Haskell:

The implementation of the selection sort by using priority queue with three different interfaces:

  • an open interface with the queue being implemented as a sorted list,
  • an abstracted interface (so the details are hidden behind the layer of abstraction),
  • a generalized interface (the details are still visible, but the implementation is more flexible).

{-# 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

The code is also available via pastebin.

Worth noticing are the existential types. As @lukstafi already pointed out, abstraction is similar to existential quantifier and generalization is similar to universal quantifier. Observe that there is a non-trivial connection between the fact that ∀x.P(x) implies ∃x.P(x) (in a non-empty universe), and that there rarely is a generalization without abstraction (even c++-like overloaded functions form a kind of abstraction in some sense).

Credits: Portal cake by Solo. Dessert table by djttwo. The symbol was based on the Portal artwork.

这篇关于抽象和泛化之间有什么区别?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆