如何修改此 Haskell 平方根函数以获取数组
How to modify this Haskell square root function to take an array
我有一个函数可以计算 int 和 return 的平方根。但是现在我想修改它,以便它接受一个整数数组并返回一个包含第一个数组元素的平方根的数组。我知道 Haskell 不使用循环,那么如何进行此修改?谢谢。
intSquareRoot :: Int -> Int
intSquareRoot n = try n where
try i | i*i > n = try (i - 1)
| i*i <= n = i
您可以使用 map
:
arraySquareRoot = map intSquareRoot
不要。
“循环遍历某些集合”,将每个结果放入其输入的相应位置的想法是一种有点微不足道但极为常见的模式。模式适用于 OO 程序员。在 Haskell 中,当有一个模式时,我们希望对其进行 抽象 ,即给它一个简单的名称,我们可以随时重复使用而无需额外的样板。
这个特殊的“模式”是仿函数操作1。对于列表,它被称为
map :: (a->b) -> [a]->[b]
更普遍(例如,它也适用于真实数组;列表实际上不是数组),
class Functor f where
fmap :: (a->b) -> f a->f b
所以不是定义一个额外的函数
intListSquareRoot :: [Int] -> [Int]
intListSquareRoot = ...
您只需在您想使用该功能的地方使用 map intSquareRoot
。
当然,您也可以定义intSquareRoot
、
的“提升”版本
intListSquareRoot = map intSquareRoot
但是与简单地在需要的地方内联 map
调用相比,这几乎没有任何好处。
如果你坚持
就是说...想知道 map
本身是如何工作的当然是有道理的。那么,您可以通过递归手动“循环”列表:
map' :: (a->b) -> [a]->[b]
map' _ [] = []
map' f (x:xs) = f x : map' f xs
现在,您可以在此处内联您的特定函数
intListSquareRoot' :: [Int] -> [Int]
intListSquareRoot' [] = []
intListSquareRoot' (x:xs) = intSquareRoot x : intListSquareRoot' xs
这不仅比快速插入 map
魔术词更笨拙和笨拙,而且通常会更慢:GHC 等编译器在处理更高级别的概念时可以进行更好的优化2 例如折叠,而不是他们必须一次又一次地使用手动定义的递归工作。
1不要混淆许多 C++ 程序员所说的“functor”. Haskell uses the word in the correct mathematical sense,它来自范畴论。
2这就是为什么像 Matlab 和 APL 这样的语言实际上在特殊应用程序中取得了不错的性能,尽管它们是动态类型的解释型语言:它们有这种“向量循环”的特殊情况被硬编码到他们的语法中。 (不幸的是,这几乎是 唯一 他们可以做好的事情...)
我有一个函数可以计算 int 和 return 的平方根。但是现在我想修改它,以便它接受一个整数数组并返回一个包含第一个数组元素的平方根的数组。我知道 Haskell 不使用循环,那么如何进行此修改?谢谢。
intSquareRoot :: Int -> Int
intSquareRoot n = try n where
try i | i*i > n = try (i - 1)
| i*i <= n = i
您可以使用 map
:
arraySquareRoot = map intSquareRoot
不要。
“循环遍历某些集合”,将每个结果放入其输入的相应位置的想法是一种有点微不足道但极为常见的模式。模式适用于 OO 程序员。在 Haskell 中,当有一个模式时,我们希望对其进行 抽象 ,即给它一个简单的名称,我们可以随时重复使用而无需额外的样板。
这个特殊的“模式”是仿函数操作1。对于列表,它被称为
map :: (a->b) -> [a]->[b]
更普遍(例如,它也适用于真实数组;列表实际上不是数组),
class Functor f where
fmap :: (a->b) -> f a->f b
所以不是定义一个额外的函数
intListSquareRoot :: [Int] -> [Int]
intListSquareRoot = ...
您只需在您想使用该功能的地方使用 map intSquareRoot
。
当然,您也可以定义intSquareRoot
、
intListSquareRoot = map intSquareRoot
但是与简单地在需要的地方内联 map
调用相比,这几乎没有任何好处。
如果你坚持
就是说...想知道 map
本身是如何工作的当然是有道理的。那么,您可以通过递归手动“循环”列表:
map' :: (a->b) -> [a]->[b]
map' _ [] = []
map' f (x:xs) = f x : map' f xs
现在,您可以在此处内联您的特定函数
intListSquareRoot' :: [Int] -> [Int]
intListSquareRoot' [] = []
intListSquareRoot' (x:xs) = intSquareRoot x : intListSquareRoot' xs
这不仅比快速插入 map
魔术词更笨拙和笨拙,而且通常会更慢:GHC 等编译器在处理更高级别的概念时可以进行更好的优化2 例如折叠,而不是他们必须一次又一次地使用手动定义的递归工作。
1不要混淆许多 C++ 程序员所说的“functor”. Haskell uses the word in the correct mathematical sense,它来自范畴论。
2这就是为什么像 Matlab 和 APL 这样的语言实际上在特殊应用程序中取得了不错的性能,尽管它们是动态类型的解释型语言:它们有这种“向量循环”的特殊情况被硬编码到他们的语法中。 (不幸的是,这几乎是 唯一 他们可以做好的事情...)