Haskell镜头库中的ix和element有什么区别

What is the difference between ix and element in the Lens library of Haskell

在Haskell的镜头库中,ixelement都带有一个Int,可以使用例如在某个索引处读取或写入列表元素,像这样

ghci> [1..10] ^? ix 4
Just 5
ghci> [1..10] & ix 4 .~ 1
[1,2,3,4,1,6,7,8,9,10]

类似地:

ghci> [1..10] ^? element 4
Just 5
ghci> [1..10] & element 4 .~ 1
[1,2,3,4,1,6,7,8,9,10]

elementix有什么区别?

使用 ix 您不仅可以按数字索引,还可以按例如地图中的键。 element 索引在 Traverse 顺序中。

λ> let m = Map.fromList [("foo", 'f'), ("bar", 'b')]
λ> m ^? ix "foo"
Just 'f'

λ> m ^? element 0 -- Map is ordered container!
Just 'b'

例如,差异更加明显。 IntMap

λ> let im = IntMap.fromList [(1, "one"), (2, "two")]
λ> im ^? ix 1
Just "one"
λ> im ^? element 1
Just "two"

element 被定义为与 Traversable class 的类型一起使用,甚至 class 镜头库不知道的成员。因此,它仅使用 Traversable 函数来访问值,这些函数没有任何特定于值类型的索引类型的概念。因此,仅支持 Int 个索引,以通常的遍历顺序给出元素。

element 还给出了一个 IndexedTraversal,给出了一些处理索引的额外方法。

ix 仅针对镜头库知道的类型定义,但在 return 中它可以根据值的类型使用不同的索引类型。

对于列表,它们给出相同的结果。但是可以看出差异,例如对于 Data.Map:

Prelude Control.Lens Data.Map> singleton "a" 3 ^? element "a"

<interactive>:19:28:
    Couldn't match expected type ‘Int’ with actual type ‘[Char]’
    In the first argument of ‘element’, namely ‘"a"’
    In the second argument of ‘(^?)’, namely ‘element "a"’
    In the expression: singleton "a" 3 ^? element "a"

Prelude Control.Lens Data.Map> singleton "a" 3 ^? ix "a"
Just 3
Prelude Control.Lens Data.Map> singleton "a" 3 ^? element 0
Just 3
Prelude Control.Lens Data.Map> singleton "a" 3 ^? ix 0

<interactive>:22:23:
    Could not deduce (Num [Char]) arising from the literal ‘0’
    from the context (Num a)
      bound by the inferred type of it :: Num a => Maybe a
      at <interactive>:22:1-23
    In the first argument of ‘ix’, namely ‘0’
    In the second argument of ‘(^?)’, namely ‘ix 0’
    In the expression: singleton "a" 3 ^? ix 0

如您所见,使用 element 时,地图被赋予 Int 索引,而使用 ix 时,它被赋予键类型作为索引,并尝试将其切换为类型错误。

element 通过遍历整个结构、计算元素并遍历具有目标索引的元素来工作。因此,它在结构大小上始终具有 O(n) 的时间复杂度,并且它仅适用于 Int 索引。

相比之下,ix有自己的classIxed,实例依赖于特定数据结构的lookup/modify操作。例如,Data.Sequenceix 是 O(log n)。

然而,ix 仅适用于特定的数据结构,而 elementOf 适用于任何 TraversalLensIso,例如:

[0..10] ^? elementOf (reversed . each . filtered odd) 1
-- Just 7