Haskell findIndices函数的解释
我说我不理解的源代码 事实上,我没有给予足够的关注,也没有看到这个函数有两种定义:Haskell findIndices函数的解释,haskell,Haskell,我说我不理解的源代码 事实上,我没有给予足够的关注,也没有看到这个函数有两种定义: findIndices :: (a -> Bool) -> [a] -> [Int] #if defined(USE_REPORT_PRELUDE) findIndices p xs = [ i | (x,i) <- zip xs [0..], p x] #else -- Efficient definition, adapted from Data.Sequence {-# I
findIndices :: (a -> Bool) -> [a] -> [Int]
#if defined(USE_REPORT_PRELUDE)
findIndices p xs = [ i | (x,i) <- zip xs [0..], p x]
#else
-- Efficient definition, adapted from Data.Sequence
{-# INLINE findIndices #-}
findIndices p ls = build $ \c n ->
let go x r k | p x = I# k `c` r (k +# 1#)
| otherwise = r (k +# 1#)
in foldr go (\_ -> n) ls 0#
#endif /* USE_REPORT_PRELUDE */
findIndices::(a->Bool)->[a]->[Int]
#如果定义(使用报告前奏)
findindicespxs=[i |(x,i)
放手xRk | px=I#k`c`r(k+#1#)
|否则=r(k+#1#)
在foldr go(\ \ \->n)ls 0中#
#endif/*使用_报告_序曲*/
我理解第一个定义,我没有看到。我不理解第二个。我有几个问题:
- 如果定义了什么(使用报告前奏)
- 你能解释一下第二个定义吗?什么是
,build
,I
,+
1
- 为什么第二个定义是内联的,而不是第一个
- 对于C编程语言,
CPP
扩展启用C预处理器。这里,它用于测试编译过程中是否设置了标志USE\u REPORT\u PRELUDE
。根据该标志,编译器使用代码的\if
或\else
变体
build
是一个可以定义为
build f = f (:) []
因此,使用build(\n->…
基本上可以将c
转换为“cons”(:)
,将n
转换为“nil”[]
这并不是为了方便而使用的:它一点也不方便!但是,编译器优化器在build
和foldr
的组合下工作得非常好,因此这里的代码是以一种奇怪的方式编写的,以利用这一点
此外,
I#…
是整数的低级构造函数
x :: Int
x = 4+2
GHC实现x
(非常粗略地)时使用一个指向某个内存的指针,该内存读作unevaluated:4+2
。在第一次强制执行x
后,该内存将被evaluated:I#6#
覆盖。这是实现惰性所必需的。
这里的“装箱”是指通过指针的间接寻址
相反,类型Int#
是一个普通的机器整数,没有指针,没有间接寻址,没有未赋值的表达式。它是严格的(而不是惰性的),但级别越低,效率就越高
x' :: Int#
x' = 6#
x :: Int
x = I# x'
实际上,Int
被定义为newtype Int=I#Int#
请记住,这不是标准的Haskell,而是GHC特定的低级详细信息。在普通代码中,您不需要使用此类未绑定类型。在库中,作者这样做是为了获得更高的性能,但仅此而已
有时,即使在我们的代码中我们只使用Int
s,GHC也足够聪明,可以自动将我们的代码转换为使用Int
,并实现更高的效率,避免装箱。如果我们要求GHC“转储核心”,以便我们可以看到优化的结果,就可以观察到这一点
例如,编译
f :: Int -> Int
f 0 = 0
f n = n + f (n-1)
GHC生成一个较低级别的版本(这是GHC核心,不是Haskell,但它非常相似,可以理解):
与C编程语言一样,
CPP
扩展支持C预处理器。这里,它用于测试编译期间是否设置了标志USE\u REPORT\u PRELUDE
。根据该标志,编译器使用代码的\if
或\else
变体
build
是一个可以定义为
build f = f (:) []
因此,使用build(\n->…
基本上可以将c
转换为“cons”(:)
,将n
转换为“nil”[]
这并不是为了方便而使用的:它一点也不方便!但是,编译器优化器在build
和foldr
的组合下工作得非常好,因此这里的代码是以一种奇怪的方式编写的,以利用这一点
此外,
I#…
是整数的低级构造函数
x :: Int
x = 4+2
GHC实现x
(非常粗略地)时使用一个指向某个内存的指针,该内存读作unevaluated:4+2
。在第一次强制执行x
后,该内存将被evaluated:I#6#
覆盖。这是实现惰性所必需的。
这里的“装箱”是指通过指针的间接寻址
相反,类型Int#
是一个普通的机器整数,没有指针,没有间接寻址,没有未赋值的表达式。它是严格的(而不是惰性的),但级别越低,效率就越高
x' :: Int#
x' = 6#
x :: Int
x = I# x'
实际上,Int
被定义为newtype Int=I#Int#
请记住,这不是标准的Haskell,而是GHC特定的低级详细信息。在普通代码中,您不需要使用此类未绑定类型。在库中,作者这样做是为了获得更高的性能,但仅此而已
有时,即使在我们的代码中我们只使用Int
s,GHC也足够聪明,可以自动将我们的代码转换为使用Int
,并实现更高的效率,避免装箱。如果我们要求GHC“转储核心”,以便我们可以看到优化的结果,就可以观察到这一点
例如,编译
f :: Int -> Int
f 0 = 0
f n = n + f (n-1)
GHC生成一个较低级别的版本(这是GHC核心,不是Haskell,但它非常相似,可以理解):
请注意
go
go x r k=…
==go x r=\k->…
。这是在折叠列表时安排从左到右信息流的标准技巧(go
用作缩减函数,在foldr go(\\\\\->n)ls 0
)。这里是[0]的计数
,解释为每个步骤的首字母k=0
和(k+1)
(k
是一个不幸的命名选择,i
似乎更好;k
被不相关的“常量”和“延续”重载,而不仅仅是“计数器”w