在从Haskell for Great Good读å–剪辑时,我å‘现了以下情况:
treeInsert :: (Ord a) => a -> Tree a -> Tree a
treeInsert x EmptyTree = singleton x
treeInsert x (Node a left right)
| x == a = Node x left right
| x < a = Node a (treeInsert x left) right
| x > a = Node a left (treeInsert x right)
如果我们在x == a
æ—¶é‡æ–°ä½¿ç”¨ç»™å®šçš„æ ‘ï¼Œé‚£ä¹ˆæ€§èƒ½ä¼šæ›´å¥½å—?
treeInsert :: (Ord a) => a -> Tree a -> Tree a
treeInsert x EmptyTree = singleton x
treeInsert x all@(Node a left right)
| x == a = all
| x < a = Node a (treeInsert x left) right
| otherwise = Node a left (treeInsert x right)
在现实生活ä¸çš„ç¼–ç ä¸ï¼Œæˆ‘该怎么办?返回åŒæ ·çš„东西有什么缺点å—?
ç”案 0 :(得分:6)
è®©æˆ‘ä»¬çœ‹çœ‹æ ¸å¿ƒï¼ ï¼ˆè¿™é‡Œæ²¡æœ‰ä¼˜åŒ–ï¼‰
 Â$ ghc-7.8.2 -ddump-simpl wtmpf-file13495.hs
相关的区别是第一个版本(没有all@(...)
)有
case GHC.Classes.> @ a_aUH $dOrd_aUV eta_B2 a1_aBQ
of _ [Occ=Dead] {
GHC.Types.False ->
Control.Exception.Base.patError
@ (TreeInsert.Tree a_aUH)
"wtmpf-file13495.hs:(9,1)-(13,45)|function treeInsert"#;
GHC.Types.True ->
TreeInsert.Node
@ a_aUH
a1_aBQ
left_aBR
(TreeInsert.treeInsert @ a_aUH $dOrd_aUV eta_B2 right_aBS)
é‡æ–°ä½¿ç”¨å¸¦æœ‰as-pattern的节点åªæ˜¯
TreeInsert.Node
@ a_aUI
a1_aBR
left_aBS
(TreeInsert.treeInsert @ a_aUI $dOrd_aUW eta_B2 right_aBT);
这是一ç§é¿å…检查,å¯èƒ½ä¼šäº§ç”Ÿæ˜¾ç€çš„性能差异。
然而,这ç§å·®å¼‚实际上与as-patternæ— å…³ã€‚è¿™åªæ˜¯å› ä¸ºä½ çš„ç¬¬ä¸€ä¸ªç‰‡æ®µä½¿ç”¨çš„æ˜¯x > a
åŽå«ï¼Œè¿™å¹¶éžæ˜“事。第二个使用otherwise
,它已ç»è¿‡ä¼˜åŒ–。
如果您将第一个代ç 段更改为
treeInsert :: (Ord a) => a -> Tree a -> Tree a
treeInsert x EmptyTree = singleton x
treeInsert x (Node a left right)
| x == a = Node x left right
| x < a = Node a (treeInsert x left) right
| otherwise = Node a left (treeInsert x right)
然åŽå·®å¼‚归结为
GHC.Types.True -> TreeInsert.Node @ a_aUH a1_aBQ left_aBR right_aBS
VS
GHC.Types.True -> wild_Xa
这确实åªæ˜¯Node x left right
与all
的区别。
......æ²¡æœ‰ä¼˜åŒ–ï¼Œå°±æ˜¯è¿™æ ·ã€‚ The versions diverge further when I turn on -O2
ã€‚ä½†æˆ‘æ— æ³•ç¡®å®šè¡¨çŽ°å¦‚ä½•ä¸åŒï¼Œé‚£é‡Œã€‚
ç”案 1 :(得分:4)
 Â在现实生活ä¸çš„ç¼–ç ä¸ï¼Œæˆ‘该怎么办?返回åŒæ ·çš„东西有什么缺点å—?
a == b
ä¸ä¿è¯æ‰€æœ‰å‡½æ•°f a == f b
都f
ã€‚æ‰€ä»¥ï¼Œä½ å¯èƒ½å¿…须返回新对象,å³ä½¿ä»–们比较相ç‰ã€‚
æ¢å¥è¯è¯´ï¼Œæ— 论性能æå‡å¦‚何,Node x left right
Node a left right
或all
都å¯èƒ½
例如,您å¯èƒ½æœ‰æºå¸¦å…ƒæ•°æ®çš„类型。当您将它们进行相ç‰æ€§æ¯”较时,您å¯èƒ½åªå…³å¿ƒå€¼å¹¶å¿½ç•¥å…ƒæ•°æ®ã€‚ä½†å¦‚æžœä½ åªæ˜¯å› 为它们相ç‰è€Œå–代它们就会失去元数æ®ã€‚
a == x
é‡ç‚¹æ˜¯Eq
type-class 表示您å¯ä»¥æ¯”较相ç‰çš„值。 除æ¤ä¹‹å¤–ä¸ä¿è¯ä»»ä½•äº‹æƒ…。
一个真实示例,其ä¸newtype ValMeta a b = ValMeta (a, b) -- value, along with meta data
deriving (Show)
instance Eq a => Eq (ValMeta a b) where
-- equality only compares values, ignores meta data
ValMeta (a, b) == ValMeta (a', b') = a == a'
ä¸ä¿è¯a == b
æ˜¯åœ¨è‡ªå¹³è¡¡æ ‘ä¸ç»´æŠ¤f a == f b
ä¸ªå”¯ä¸€å€¼çš„æ—¶å€™ã€‚è‡ªå¹³è¡¡æ ‘ï¼ˆä¾‹å¦‚çº¢é»‘æ ‘ï¼‰å¯¹æ ‘çš„ç»“æž„æœ‰ä¸€äº›ä¿è¯ï¼Œä½†å®žé™…深度和结构å–决于数æ®è¢«æ·»åŠ 到集åˆä¸æˆ–从集åˆä¸ç§»é™¤çš„顺åºã€‚
现在,当您比较2个集åˆçš„相ç‰æ€§æ—¶ï¼Œæ‚¨å¸Œæœ›æ¯”较集åˆä¸çš„值是å¦ç›¸ç‰ï¼Œè€Œä¸æ˜¯åŸºç¡€æ ‘具有相åŒçš„ç¡®åˆ‡ç»“æž„ã€‚ä½†æ˜¯å¦‚æžœä½ æœ‰ä¸€ä¸ªåƒSet
è¿™æ ·çš„å‡½æ•°æš´éœ²äº†ç»´æŠ¤é›†åˆçš„åº•å±‚æ ‘çš„æ·±åº¦ï¼Œé‚£ä¹ˆå³ä½¿é›†åˆæ¯”较相ç‰ï¼Œä¹Ÿæ— 法ä¿è¯æ·±åº¦ç›¸ç‰ã€‚
Here is a video of great Philip Wadler realizing live and on-stage that many useful relations do not preserve equality(从42分钟开始)。
编辑:æ¥è‡ªghc的示例depth
并ä¸æš—示a == b
:
f a == f b
å¦ä¸€ä¸ªçŽ°å®žä¸–界的例å是哈希表。当且仅当它们的键值对结åˆæ—¶ï¼Œä¸¤ä¸ªå“ˆå¸Œè¡¨æ˜¯ç›¸ç‰çš„。但是,哈希表的 capacity ,å³æ‚¨å¿…须在é‡æ–°åˆ†é…å’Œé‡æ–°åˆ†é…之å‰æ·»åŠ 的密钥数é‡ï¼Œå–决于æ’å…¥/åˆ é™¤çš„é¡ºåºã€‚
å› æ¤ï¼Œå¦‚果您有一个返回哈希表容é‡çš„函数,它å¯èƒ½ä¼šä¸ºå“ˆå¸Œè¡¨\> import Data.Set
\> let a = fromList [1, 2, 3, 4, 5, 10, 9, 8, 7, 6]
\> let b = fromList [1..10]
\> let f = showTree
\> a == b
True
\> f a == f b
False
å’Œa
返回ä¸åŒçš„值,å³ä½¿b
。
ç”案 2 :(得分:3)
我的两分钱......或许甚至ä¸æ˜¯åŽŸæ¥çš„问题:
我ä¸ä¼šä½¿ç”¨x < a
å’Œx == a
编写è¦å«ï¼Œè€Œæ˜¯å°†compare a b
与LT
,EQ
å’ŒGT
匹é…,例如:< / p>
treeInsert x all@(Node a left right) =
case compare x a of
EQ -> ...
LT -> ...
GT -> ...
如果x
å’Œa
å¯èƒ½æ˜¯å¤æ‚çš„æ•°æ®ç»“æž„ï¼Œæˆ‘ä¼šè¿™æ ·åšï¼Œå› 为åƒx < a
è¿™æ ·çš„æµ‹è¯•å¯èƒ½ä¼šå¾ˆæ˜‚贵。
ç”案 3 :(得分:2)
ç”案似乎是错误的。我把它留在这里,供å‚考......
使用第二个函数å¯ä»¥é¿å…åˆ›å»ºæ–°èŠ‚ç‚¹ï¼Œå› ä¸ºç¼–è¯‘å™¨æ— æ³•çœŸæ£ç†è§£ç›¸ç‰ï¼ˆ==
åªæ˜¯ä¸€äº›å‡½æ•°ã€‚)如果将第一个版本更改为
-- version C
treeInsert :: (Ord a) => a -> Tree a -> Tree a
treeInsert x EmptyTree = singleton x
treeInsert x (Node a left right)
| x == a = Node a left right -- Difference here! Changed x to a.
| x < a = Node a (treeInsert x left) right
| x > a = Node a left (treeInsert x right)
编译器å¯èƒ½ä¼šæ‰§è¡Œå¸¸è§çš„å表达å¼æ¶ˆé™¤ï¼Œå› 为优化器将能够看到Node a left right
与Node a left right
相åŒã€‚
å¦ä¸€æ–¹é¢ï¼Œæˆ‘怀疑编译器å¯ä»¥ä»Ža == x
推æ–Node a left right
与Node x left right
相åŒã€‚
å› æ¤ï¼Œæˆ‘éžå¸¸ç¡®å®šåœ¨-O2
下,版本B和版本C是相åŒçš„,但版本Aå¯èƒ½æ›´æ…¢ï¼Œå› 为它在a == x
情况下进行了é¢å¤–的实例化。
ç”案 4 :(得分:2)
好å§ï¼Œå¦‚果第一个案例使用了a
而ä¸æ˜¯x
,那么至少GHCå¯ä»¥é€šè¿‡å…¬å…±å表达å¼æ¶ˆé™¤æ¥æ¶ˆé™¤æ–°èŠ‚点的分é…。
treeInsert x (Node a left right)
| x == a = Node a left right
然而,这在任何éžå¹³å‡¡çš„用例ä¸éƒ½æ˜¯æ— 关紧è¦çš„ï¼Œå› ä¸ºå³ä½¿å…ƒç´ å·²ç»å˜åœ¨ï¼Œæ ‘下到节点的路径也将被å¤åˆ¶ã€‚除éžä½ 的用例很简å•ï¼Œå¦åˆ™è¿™æ¡è·¯å¾„将明显长于å•ä¸ªèŠ‚点。
在ML的世界ä¸ï¼Œé¿å…è¿™ç§æƒ…况的相当惯用的方法是抛出KeyAlreadyExists
异常,然åŽåœ¨é¡¶çº§æ’入函数ä¸æ•èŽ·è¯¥å¼‚å¸¸å¹¶è¿”å›žåŽŸå§‹æ ‘ã€‚è¿™å°†å¯¼è‡´å †æ ˆå±•å¼€ï¼Œè€Œä¸æ˜¯åœ¨å †ä¸Šåˆ†é…任何Node
。
直接实现MLä¹ è¯åœ¨Haskellä¸åŸºæœ¬ä¸Šæ˜¯ç¦æ¢çš„ï¼ŒåŽŸå› å¾ˆå……åˆ†ã€‚å¦‚æžœé¿å…è¿™ç§é‡å¤å¾ˆé‡è¦ï¼Œæœ€ç®€å•ä¹Ÿå¯èƒ½æœ€å¥½çš„åšæ³•æ˜¯åœ¨æ’入之å‰æ£€æŸ¥æ ‘是å¦åŒ…å«å¯†é’¥ã€‚
与直接Haskellæ’入或MLä¹ è¯ç›¸æ¯”,这ç§æ–¹æ³•çš„缺点是它涉åŠä¸¤æ¬¡é历路径而ä¸æ˜¯ä¸€æ¬¡ã€‚现在,这是一个å¯ä»¥åœ¨Haskellä¸å®žçŽ°çš„éžé‡å¤çš„å•éæ’入:
treeInsert :: Ord a => a -> Tree a -> Tree a
treeInsert x original_tree = result_tree
where
(result_tree, new_tree) = loop x original_tree
loop x EmptyTree = (new_tree, singleton x)
loop x (Node a left right) =
case compare x a of
LT -> let (res, new_left) = loop x left
in (res, Node a new_left right)
EQ -> (original_tree, error "unreachable")
GT -> let (res, new_right) = loop x right
in (res, Node a left new_right)
然而,旧版本的GHC(大约7到10å¹´å‰ï¼‰ä¸èƒ½éžå¸¸æœ‰æ•ˆåœ°é€šè¿‡æƒ°æ€§ç»“果对处ç†è¿™ç§é€’å½’ï¼Œå¹¶ä¸”æ ¹æ®æˆ‘çš„ç»éªŒï¼Œåœ¨æ’å…¥å‰æ£€æŸ¥å¯èƒ½ä¼šè¡¨çŽ°å¾—更好。如果这个观察结果在最近的GHC版本的背景下真的å‘生了å˜åŒ–,我会感到有些惊讶。
当然å¯ä»¥æƒ³è±¡ä¸€ä¸ªå‡½æ•°ç›´æŽ¥æž„é€ ï¼ˆä½†ä¸è¿”å›žï¼‰æ ‘çš„æ–°è·¯å¾„ï¼Œå¹¶ä¸”ä¸€æ—¦çŸ¥é“å…ƒç´ æ˜¯å¦å·²ç»å˜åœ¨å°±å†³å®šè¿”回新路径或原始路径。 (如果没有返回,新路径将立å³å˜ä¸ºåžƒåœ¾ã€‚)这符åˆGHCè¿è¡Œæ—¶çš„基本原则,但在æºè¯è¨€ä¸å¹¶ä¸èƒ½çœŸæ£è¡¨è¾¾ã€‚
当然,对于惰性数æ®ç»“构,任何完全ä¸é‡å¤çš„æ’入函数都将具有与简å•çš„é‡å¤æ’å…¥ä¸åŒçš„ä¸¥æ ¼æ€§ã€‚å› æ¤ï¼Œæ— 论实现技术如何,如果懒惰都很é‡è¦ï¼Œå®ƒä»¬å°±ä¼šæœ‰ä¸åŒçš„功能。
但是,当然,路径是å¦é‡å¤å¯èƒ½å¹¶ä¸é‡è¦ã€‚最é‡è¦çš„æƒ…å†µæ˜¯å½“ä½ æŒç»ä½¿ç”¨æ ‘æ—¶ï¼Œå› ä¸ºåœ¨çº¿æ€§ä½¿ç”¨æƒ…å†µä¸‹ï¼Œæ—§è·¯å¾„åœ¨æ¯æ¬¡æ’å…¥åŽä¼šç«‹å³å˜æˆåžƒåœ¾ã€‚当然,这åªåœ¨æ‚¨æ’入大é‡é‡å¤é¡¹æ—¶æ‰æœ‰æ„义。