如何分析Haskell中的一个函数?
How to analyse a function in Haskell?
大家好Haskell这里是新手。我真的很困惑如何看待柯里化函数。
例如,这里有一个函数定义
zipWith' :: (a -> b -> c) -> [a] -> [b] -> [c]
zipWith' _ [] _ = []
zipWith' _ _ [] = []
zipWith' f (x:xs) (y:ys) = f x y : zipWith' f xs ys
当我打电话时
zipWith' (zipWith' (*)) [[1,2,3],[3,5,6]] [[3,2,2],[3,4,5]]
我明白
zipWith' :: (a -> b -> c) -> [a] -> [b] -> [c]
等同于
zipWith' :: (a -> b -> c) -> ([a] -> ([b] -> [c]))
不过看不懂的是way/order。我们应该从左到右分析它吗?例如先查看 (a -> b -> c),然后将 [a] 应用于 (a -> b -> c),最后将 ([b] -> [c]) 应用于 (a -> b -> c) -> [a]?还是相反?
如果你不明白我在问什么(抱歉 :( ),你能告诉我当你看到这类问题时你是怎么想的吗?每当我看到这些函数时我都会很害怕,而且它通常花了我一段时间才弄明白:P
柯里化函数看起来比实际情况更可怕。考虑它们如何工作的一种方法是取消它们。例如:
(++) :: [t] -> [t] -> [t]
未柯里化时变为:
(++) :: ([t], [t]) -> [t]
在我看来,这就是所有需要的解释,但这里有一个更详细的解释,可能会完全解释:
a -> b
类型的函数接受类型 a
的参数,return 接受类型 b
的参数。请记住,a -> b
本身就是一个具体类型,可以首先取代 b
,从而实现类似 a -> (b -> c)
.
的功能
假设函数f
的类型为a -> b -> c
,如上。那是什么意思? 这是一个函数,return是一个函数!这就是柯里化的工作原理。函数的想法 returning 其他函数允许我们 部分应用 函数,限制或指定它们的特征。
现在让我们创建自己的函数,指定:
func :: a -> [a] -> [a]
func = \elem -> ( \list -> elem : list )
这里的 lambda 表达式的编写方式使得类型的含义应该很明显:第一个 lambda (\elem -> ...
) 根据它的输入构造另一个。
现在,让我们来看看zipWith
:
zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
zipWith
的第一个参数是一个函数,它本身有两个参数。不太疯狂。
第二个参数是一个可以传递给函数的列表,因为定义了类型变量。
第三个参数是另一个列表,可以在第一个参数之后传递给函数。
return 值是我们首先传递给 zipWith
的函数的结果类型列表。
那么,让我们从压缩功能开始吧。它的类型是a -> (b -> c)
。它以 a
类型的参数和 return 将 b
转换为 c
的函数作为参数。它与它的未柯里化等价 (a, b) -> c
同构,不同之处在于我们固定了第一个参数,以获得现在只需要 b
到 return c 的函数。
这就是拉链类型的意思。现在让我们继续 zipWith'
。它以压缩函数作为参数(如上所述)。它 return 是 [a] -> ([b] -> [c])
类型的东西,与 zipper 功能非常相似。我们可以说 zipWith'
提升了压缩功能来处理列表 -zipWith'
returns 函数 接受 a
s 的列表,并且 returns 函数获取 b
s 列表和 returns c
s 列表。
在您的示例中,我们有 zipWith' (*)
。为简单起见,假设 (*)
是 Int -> (Int -> Int)
类型(实际上它更通用 - (Num a) => a -> (a -> a
)。然后我们有 a ~ b ~ c ~ Int
和 zipWith' (*) :: [Int] -> ([Int] -> [Int])
。然后 zipWith' (*)
将 Int
的列表作为参数,return 将一个 Int
的列表转换为另一个的函数。所以它可以用来压缩两个 a
列表的列表。而这正是发生的事情。
在这种情况下,将嵌套列表视为(二维)矩阵可能会有所帮助。
A = ( 1 2 3 ) B = ( 3 2 2 ) --> result = ( 3 4 6 )
( 3 5 6 ) ( 3 4 5 ) ( 9 20 30 )
外部 zip' 将 A 和 B 中的行组合在一起,而内部 zip 将一对行组合成另一行,因此很明显结果将与输入具有相同的形状。
认识到 zipWith (*)
只是向量的分量乘法也可能会有所帮助。其实是有规律的:
zipWith (*) -- component-wise * on 1-d matrices
zipWith (zipWith (*)) -- component-wise * on 2-d matrices
zipWith (zipWith (zipWith (*))) -- component-wise * on 3-d matrices
...
当然还有(*)
可以用任何二元运算代替。
其他一些列表函数及其矩阵解释:
- map - 遍历所有行
- transpose - 转置行和列
- concat - 展平第一个维度(即将所有行合并为一行)
作为练习,您可以尝试使用列表运算实现矩阵乘法。
从左到右。
其实你可以从4个不同的角度来看它
zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
-- take a function, 2 list and returns a list
zipWith (*) :: [a] -> [b] -> [c]
-- new function which takes 2 list and returns a new one
zipWith (*) [1,2,3] :: [b] -> [c]
-- new function which takes a list a produce a new one
zipPwith * [1,2,3] [3,5,6] :: [c]
-- a function without argument, or a value (equivalent in Haskell)
或从右到左:-)
zipPwith * [1,2,3] [3,5,6] :: [c]
-- a function without argument, or a value (equivalent in Haskell)
zipWith (*) [1,2,3] :: [b] -> [c]
-- new function which takes a list a produce a new one
zipWith (*) :: [a] -> [b] -> [c]
-- new function which takes 2 list and returns a new one
zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
-- take a function, 2 list and returns a list
大家好Haskell这里是新手。我真的很困惑如何看待柯里化函数。
例如,这里有一个函数定义
zipWith' :: (a -> b -> c) -> [a] -> [b] -> [c]
zipWith' _ [] _ = []
zipWith' _ _ [] = []
zipWith' f (x:xs) (y:ys) = f x y : zipWith' f xs ys
当我打电话时
zipWith' (zipWith' (*)) [[1,2,3],[3,5,6]] [[3,2,2],[3,4,5]]
我明白
zipWith' :: (a -> b -> c) -> [a] -> [b] -> [c]
等同于
zipWith' :: (a -> b -> c) -> ([a] -> ([b] -> [c]))
不过看不懂的是way/order。我们应该从左到右分析它吗?例如先查看 (a -> b -> c),然后将 [a] 应用于 (a -> b -> c),最后将 ([b] -> [c]) 应用于 (a -> b -> c) -> [a]?还是相反?
如果你不明白我在问什么(抱歉 :( ),你能告诉我当你看到这类问题时你是怎么想的吗?每当我看到这些函数时我都会很害怕,而且它通常花了我一段时间才弄明白:P
柯里化函数看起来比实际情况更可怕。考虑它们如何工作的一种方法是取消它们。例如:
(++) :: [t] -> [t] -> [t]
未柯里化时变为:
(++) :: ([t], [t]) -> [t]
在我看来,这就是所有需要的解释,但这里有一个更详细的解释,可能会完全解释:
a -> b
类型的函数接受类型 a
的参数,return 接受类型 b
的参数。请记住,a -> b
本身就是一个具体类型,可以首先取代 b
,从而实现类似 a -> (b -> c)
.
假设函数f
的类型为a -> b -> c
,如上。那是什么意思? 这是一个函数,return是一个函数!这就是柯里化的工作原理。函数的想法 returning 其他函数允许我们 部分应用 函数,限制或指定它们的特征。
现在让我们创建自己的函数,指定:
func :: a -> [a] -> [a]
func = \elem -> ( \list -> elem : list )
这里的 lambda 表达式的编写方式使得类型的含义应该很明显:第一个 lambda (\elem -> ...
) 根据它的输入构造另一个。
现在,让我们来看看zipWith
:
zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
zipWith
的第一个参数是一个函数,它本身有两个参数。不太疯狂。
第二个参数是一个可以传递给函数的列表,因为定义了类型变量。
第三个参数是另一个列表,可以在第一个参数之后传递给函数。
return 值是我们首先传递给 zipWith
的函数的结果类型列表。
那么,让我们从压缩功能开始吧。它的类型是a -> (b -> c)
。它以 a
类型的参数和 return 将 b
转换为 c
的函数作为参数。它与它的未柯里化等价 (a, b) -> c
同构,不同之处在于我们固定了第一个参数,以获得现在只需要 b
到 return c 的函数。
这就是拉链类型的意思。现在让我们继续 zipWith'
。它以压缩函数作为参数(如上所述)。它 return 是 [a] -> ([b] -> [c])
类型的东西,与 zipper 功能非常相似。我们可以说 zipWith'
提升了压缩功能来处理列表 -zipWith'
returns 函数 接受 a
s 的列表,并且 returns 函数获取 b
s 列表和 returns c
s 列表。
在您的示例中,我们有 zipWith' (*)
。为简单起见,假设 (*)
是 Int -> (Int -> Int)
类型(实际上它更通用 - (Num a) => a -> (a -> a
)。然后我们有 a ~ b ~ c ~ Int
和 zipWith' (*) :: [Int] -> ([Int] -> [Int])
。然后 zipWith' (*)
将 Int
的列表作为参数,return 将一个 Int
的列表转换为另一个的函数。所以它可以用来压缩两个 a
列表的列表。而这正是发生的事情。
在这种情况下,将嵌套列表视为(二维)矩阵可能会有所帮助。
A = ( 1 2 3 ) B = ( 3 2 2 ) --> result = ( 3 4 6 )
( 3 5 6 ) ( 3 4 5 ) ( 9 20 30 )
外部 zip' 将 A 和 B 中的行组合在一起,而内部 zip 将一对行组合成另一行,因此很明显结果将与输入具有相同的形状。
认识到 zipWith (*)
只是向量的分量乘法也可能会有所帮助。其实是有规律的:
zipWith (*) -- component-wise * on 1-d matrices
zipWith (zipWith (*)) -- component-wise * on 2-d matrices
zipWith (zipWith (zipWith (*))) -- component-wise * on 3-d matrices
...
当然还有(*)
可以用任何二元运算代替。
其他一些列表函数及其矩阵解释:
- map - 遍历所有行
- transpose - 转置行和列
- concat - 展平第一个维度(即将所有行合并为一行)
作为练习,您可以尝试使用列表运算实现矩阵乘法。
从左到右。
其实你可以从4个不同的角度来看它
zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
-- take a function, 2 list and returns a list
zipWith (*) :: [a] -> [b] -> [c]
-- new function which takes 2 list and returns a new one
zipWith (*) [1,2,3] :: [b] -> [c]
-- new function which takes a list a produce a new one
zipPwith * [1,2,3] [3,5,6] :: [c]
-- a function without argument, or a value (equivalent in Haskell)
或从右到左:-)
zipPwith * [1,2,3] [3,5,6] :: [c]
-- a function without argument, or a value (equivalent in Haskell)
zipWith (*) [1,2,3] :: [b] -> [c]
-- new function which takes a list a produce a new one
zipWith (*) :: [a] -> [b] -> [c]
-- new function which takes 2 list and returns a new one
zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
-- take a function, 2 list and returns a list