我正在努力将我的服务器端编程语言从PHP修改为Haskell,因此我并不完全熟悉函数式语言设计。
这里我想将以下php函数的部分处理代码转换为Haskell:
function loop($A) {
$array1 = array();
$num = (int)$A;
if ($num == 0)
$array1 = "0";
for (int $i=0; $i<$num; $i++)
foreach (loop($i-$num) as &$value)
$array1[] = "+ ".$value." "; //concat
return $array1;
}
我想会有大量使用map / mapM_函数,但积累如何工作?我开始认为增加的安全性在这一点上不值得改造。
感谢。
答案 0 :(得分:6)
这是我正在回答的时候的PHP:
function loop($A) {
$array1 = array();
$num = (int)$A;
if ($num == 0)
$array1 = "0";
for (int $i=0; $i<$num; $i++)
foreach (loop($i-$num) as &$value)
$array1[] = "+ ".$value." "; //concat
return $array1;
}
上述代码到Haskell的字面翻译将是:
loop :: Int -> [String]
loop 0 = ["0"]
loop num = ["+ " ++ value ++ " " | i <- [0..num], value <- loop (i - num)]
它仍然没有实现任何目标,但它确实给你带来了味道 Haskell在列表推导中的“循环”或迭代。
首先要注意的是类型签名loop :: Int -> [String]
提醒我写loop 0 = ["0"]
而不是loop 0 = "0"
,这更像是字面上的
你有什么,但Haskell发现循环的输出如果它不一致
有时是一个字符串列表,有时只是一个字符串。我认为这是一种错误
检查它值得学习 - 也许修改时编写的所有代码都会在php中编译,
但由于设计不一致,Haskell 不会 - 它会迫使你在写作时清楚地思考
并在编译时修复bug。
我将解释我的代码,不是作为Haskell的完整解释,而是作为一个品尝者并将其与您的代码相关联:
loop :: Int -> [String]
loop
是一个函数,它接受一个Int
eger并返回一个字符串列表。
Haskell现在不允许您将此与任何其他数据类型一起使用,这意味着它的类型安全,并且您受到保护
来自各种令人讨厌的虫子直接开箱即用。类型系统实际上非常灵活,富有表现力
而且功能强大,是我们喜欢Haskell的原因之一,但是我在这里使用它来锁定功能。
(列表在Haskell而不是数组中广泛使用。它们非常方便,并且可以快速进行顺序访问。 有数组数据类型,但它们不太可爱/ Haskellish - 首先习惯列表。)
loop 0 = ["0"]
这意味着如果您使用Int loop
调用0
,它应该返回带有一个字符串“0”的列表。
loop num = [ something | stuff... ]
表示您可以在stuff
中获取变量的值,并将其返回something
,所以
loop num = [ "+ " ++ value ++ " " | stuff... ]
意味着我们将返回(Haskell的版本)"+ ".$value." "
。在这里,我们使用++
而不是php的.
。
i <- [0..num]
这意味着让i
的范围从0
到num
。它必须是Int
,因为num
是因为loop :: Int -> ...
。
value <- loop (i - num)
这意味着让value
在调用loop
的结果中使用数字i - num
。
value
必须String
,因为loop :: Int -> [String]
。
在Haskell中,它就像递归调用一样实现为表达式重写, 所以他们在被叫的时候消失了,不要把堆栈弄得乱七八糟 除非你的计算固有地混乱了堆栈。
把它放在一起给出:
loop num = ["+ " ++ value ++ " " | i <- [0..num], value <- loop (i - num)]
如果这种符号感觉非常奇怪,你可能会更习惯于更强制的样式语法:
loop :: Int -> [String]
loop 0 = ["0"]
loop num = do
i <- [0..num]
value <- loop (i - num)
return ("+ " ++ value ++ " ")
当然仍然没有做任何有用的事情,但至少它很容易跟踪。
为什么不通过http://learnyouahaskell.com/或真实世界Haskell 在<{3}}处完成了解你的Haskell 这两个教程开始很简单但是深入。我想你会发现Haskell是编写少量正确代码以替换大量OK代码的好方法。想得更清楚,写得更少!
答案 1 :(得分:3)
看起来你有某种字典。字典的典型Haskell数据结构是Map
,由Data.Map
提供。因此,如果您的密钥为String
且价值为Int
s,那么您的地图的类型为Map String Int
。
现在,根据您的代码判断,您似乎将键和值混合在同一个数组中,因此首先我们必须将它们分成键值对。我会假装你的开始输入是一个列表而不是一个数组,虽然我在这里写的所有东西也适用于Haskell数组:
fix :: [a] -> [(a, a)]
fix [] = []
fix [_] = []
fix (k:v:xs) = (k, v):fix xs
您可以选择在关联列表中停止,在这种情况下,您可以将其转换为描述性字符串列表:
describe :: (Show k, Show v) => [(k, v)] -> [String]
describe xs = map (\(k, v) -> "Stored On: " ++ show k ++ ", item: " ++ show v) xs
或者我们可以直接打印出来:
printPairs :: (Show k, Show v) => [(k, v)] -> IO ()
printPairs xs = mapM_ putStrLn (describe xs)
现在,为了提高查询效率,您通常希望将关联列表存储为Map
,因此您只需使用fromList
中的Data.Map
函数:
fromList :: Ord k => [(k, v)] -> Map k v
如果您想将列表从地图中取出(即打印出来),您只需使用toList
:
toList :: Map k v -> [(k, v)]