(x*) 中的星号在 Haskell 中有什么作用?
What does the asterisk in (x*) do in Haskell?
我是 Haskell 的新手,有人可以向我解释这段代码的工作原理吗?
f = g (\x -> x)
g k [] = k 100
g k (x:xs) = g ((x*) . k) xs
当我调用f [1..5]
时,它是returns12000。我不明白为什么。 (x*)
是做什么的?
那是运算符部分。 (x*)
等价于 \y -> (x * y)
,一个将其自变量乘以 x
的函数。
您可以将运算符部分与 任何 运算符一起使用,而不仅仅是 *
。这包括用反引号括起来的普通函数。例如,以下所有内容都是等效的:
(3 `elem`)
(elem 3)
\xs -> 3 `elem` xs
\xs -> elem 3 ex
这是乘法。实际上这适用于 (*) :: Num a => a -> a -> a
. It uses the section of an infix operator [Haskell-wiki].
这意味着 (x *)
具有类型 Num a => a -> a
和 a
变量类型 x
,因此它将 相乘 由 x
.
给定的值
您可以扩展定义,观察发生了什么。
f [1..5]
= g (\x -> x) [1..5]
= g ((1*) . (\x -> x)) [2..5]
= g ((2*) . (1*) . (\x -> x)) [3..5]
= g ((3*) . (2*) . (1*) . (\x -> x)) [4..5]
= g ((4*) . (3*) . (2*) . (1*) . (\x -> x)) [5]
= g ((4*) . (3*) . (2*) . (1*) . (\x -> x)) [5]
= g ((5*) . (4*) . (3*) . (2*) . (1*) . (\x -> x)) []
= ((5*) . (4*) . (3*) . (2*) . (1*) . (\x -> x)) 100
= ((5*) . (4*) . (3*) . (2*) . (1*)) 100
= ((5*) . (4*) . (3*) . (2*)) 100
= ((5*) . (4*) . (3*)) 200
= ((5*) . (4*)) 600
= (5*) 2400
= 12000
注意(x*)
是函数“乘以x
”,即\y -> x*y
,而运算符.
是函数组合。所以组合链 (5*) . (4*) . ... . (1*)
是函数“将输入乘以 1,然后乘以 2,然后乘以 3,...,然后乘以 5”。
您的代码是以所谓的连续传递样式 (CPS) 编写的,我们不是从初始值 (100) 开始并对其应用函数,而是执行(尾)递归调用和“将一些函数附加到当前的“延续”k
(在您的代码中使用 (x*) . k
)。这样做会构建一长串组合函数,并且仅在最后我们将该链应用于初始值 (100)。这种代码风格不是特别好读,总的来说。
这不是某种特殊的星号符号,它只是标准的乘法运算符。
Prelude> 5*7
35
与任何中缀运算符一样,您可以使用运算符的部分:
Prelude> (5*) 7
35
Prelude> (*7) 5
35
为了更清楚地说明发生了什么,以非交换运算符为例:
Prelude> 15 / 3
5.0
Prelude> (15/) 3
5.0
Prelude> (/3) 15
5.0
一个部分的重点是你得到一个函数对应于获取第二个操作数,同时保持已经提供的操作数不变,比如
Prelude> map (/3) [21, 30, 45]
[7.0,10.0,15.0]
Prelude> map (24/) [3,4]
[8.0,6.0]
如果 x
具有类型,例如 Int
,则 (x*)
具有类型 Int -> Int
。这样的函数可以与另一个函数组合,该函数使用 .
运算符给出 Int
作为结果,例如
Prelude> ((4*) . length) "bla"
12 -- same as
Prelude> 4 * length "bla"
12
这样的组合函数不仅可以立即应用于参数,还可以映射到列表(或通常作为高阶函数的参数传递):
Prelude> map ((4*) . length) ["bla", "blub"]
[12,16]
在您的示例中,g
是这样一个高阶函数:
g :: (Int -> Int) -> [Int] -> Int
它以一个函数作为第一个参数,在调用中 g (\x -> x) [1..5]
只是恒等函数(又名 g id [1..5]
)。
但在递归调用中,g
“修改”了该函数,post-将其与乘法部分组合在一起。例如,
g id [2] ≡ g ((2*) . id) []
≡ ((2*) . id) 100
≡ (2*) 100
≡ 2 * 100
≡ 200
如果列表更长,那就是
g id [7,8] ≡ g ((7*) . id) [8]
≡ g ((8*) . ((7*) . id)) []
≡ ((8*) . ((7*) . id)) 100
≡ 8 * 7 * id 100
≡ 5600
我是 Haskell 的新手,有人可以向我解释这段代码的工作原理吗?
f = g (\x -> x)
g k [] = k 100
g k (x:xs) = g ((x*) . k) xs
当我调用f [1..5]
时,它是returns12000。我不明白为什么。 (x*)
是做什么的?
那是运算符部分。 (x*)
等价于 \y -> (x * y)
,一个将其自变量乘以 x
的函数。
您可以将运算符部分与 任何 运算符一起使用,而不仅仅是 *
。这包括用反引号括起来的普通函数。例如,以下所有内容都是等效的:
(3 `elem`)
(elem 3)
\xs -> 3 `elem` xs
\xs -> elem 3 ex
这是乘法。实际上这适用于 (*) :: Num a => a -> a -> a
. It uses the section of an infix operator [Haskell-wiki].
这意味着 (x *)
具有类型 Num a => a -> a
和 a
变量类型 x
,因此它将 相乘 由 x
.
您可以扩展定义,观察发生了什么。
f [1..5]
= g (\x -> x) [1..5]
= g ((1*) . (\x -> x)) [2..5]
= g ((2*) . (1*) . (\x -> x)) [3..5]
= g ((3*) . (2*) . (1*) . (\x -> x)) [4..5]
= g ((4*) . (3*) . (2*) . (1*) . (\x -> x)) [5]
= g ((4*) . (3*) . (2*) . (1*) . (\x -> x)) [5]
= g ((5*) . (4*) . (3*) . (2*) . (1*) . (\x -> x)) []
= ((5*) . (4*) . (3*) . (2*) . (1*) . (\x -> x)) 100
= ((5*) . (4*) . (3*) . (2*) . (1*)) 100
= ((5*) . (4*) . (3*) . (2*)) 100
= ((5*) . (4*) . (3*)) 200
= ((5*) . (4*)) 600
= (5*) 2400
= 12000
注意(x*)
是函数“乘以x
”,即\y -> x*y
,而运算符.
是函数组合。所以组合链 (5*) . (4*) . ... . (1*)
是函数“将输入乘以 1,然后乘以 2,然后乘以 3,...,然后乘以 5”。
您的代码是以所谓的连续传递样式 (CPS) 编写的,我们不是从初始值 (100) 开始并对其应用函数,而是执行(尾)递归调用和“将一些函数附加到当前的“延续”k
(在您的代码中使用 (x*) . k
)。这样做会构建一长串组合函数,并且仅在最后我们将该链应用于初始值 (100)。这种代码风格不是特别好读,总的来说。
这不是某种特殊的星号符号,它只是标准的乘法运算符。
Prelude> 5*7
35
与任何中缀运算符一样,您可以使用运算符的部分:
Prelude> (5*) 7
35
Prelude> (*7) 5
35
为了更清楚地说明发生了什么,以非交换运算符为例:
Prelude> 15 / 3
5.0
Prelude> (15/) 3
5.0
Prelude> (/3) 15
5.0
一个部分的重点是你得到一个函数对应于获取第二个操作数,同时保持已经提供的操作数不变,比如
Prelude> map (/3) [21, 30, 45]
[7.0,10.0,15.0]
Prelude> map (24/) [3,4]
[8.0,6.0]
如果 x
具有类型,例如 Int
,则 (x*)
具有类型 Int -> Int
。这样的函数可以与另一个函数组合,该函数使用 .
运算符给出 Int
作为结果,例如
Prelude> ((4*) . length) "bla"
12 -- same as
Prelude> 4 * length "bla"
12
这样的组合函数不仅可以立即应用于参数,还可以映射到列表(或通常作为高阶函数的参数传递):
Prelude> map ((4*) . length) ["bla", "blub"]
[12,16]
在您的示例中,g
是这样一个高阶函数:
g :: (Int -> Int) -> [Int] -> Int
它以一个函数作为第一个参数,在调用中 g (\x -> x) [1..5]
只是恒等函数(又名 g id [1..5]
)。
但在递归调用中,g
“修改”了该函数,post-将其与乘法部分组合在一起。例如,
g id [2] ≡ g ((2*) . id) []
≡ ((2*) . id) 100
≡ (2*) 100
≡ 2 * 100
≡ 200
如果列表更长,那就是
g id [7,8] ≡ g ((7*) . id) [8]
≡ g ((8*) . ((7*) . id)) []
≡ ((8*) . ((7*) . id)) 100
≡ 8 * 7 * id 100
≡ 5600