在Haskell中将元组的元素作为参数提供给函数?

时间:2011-02-25 13:45:55

标签: haskell functional-programming printf currying

在我的Haskell程序中,我想使用printf来格式化元组列表。我可以将printf映射到列表上,一次打印一个值,如下所示:

mapM_ (printf "Value: %d\n") [1,2,3,4]

Value: 1
Value: 2
Value: 3
Value: 4

我希望能够做到这样的事情:

mapM_ (printf "Values: %d %d\n") [(1,100),(2,350),(3,600),(4,200)]

Values: 1 100
Values: 2 350
Values: 3 600
Values: 4 200

但是这会将一个元组传递给printf,而不是两个单独的值。如何将元组转换为printf的两个参数?

3 个答案:

答案 0 :(得分:41)

函数uncurry将双参数(curried)函数转换为对上的函数。这是它的类型签名:

uncurry :: (a -> b -> c) -> (a, b) -> c

您需要在printf上使用它,如下所示:

mapM_ (uncurry $ printf "Values: %d %d\n") [(1,100),(2,350),(3,600),(4,200)]

另一个解决方案是使用模式匹配来解构元组,如下所示:

mapM_ (\(a,b) -> printf "Values: %d %d\n" a b) [(1,100),(2,350),(3,600),(4,200)]

答案 1 :(得分:3)

mapM_ (\(x,y) -> printf "Value: %d %d\n" x y) [(1,100),(2,350),(3,600),(4,200)]

答案 2 :(得分:2)

Text.Printf的类型安全替代方法是formatting包。 Text.Printf.printf无法在编译时确保格式化参数的数量与参数的数量及其类型一致。阅读Chris Done的文章What's wrong with printf?作为例子。

示例用法:

{-# LANGUAGE OverloadedStrings #-}
import Formatting

map (uncurry $ formatToString ("Value: " % int % " " % int)) [(1,100), (2,350), ...]
map (\(x,y) -> formatToString ("Value: " % int % " " % int) x y) [(1,100), (2,350), ...]

它需要GHC扩展OverloadedStrings才能正常运行。

虽然formatToString ("Value: " % int % " " % int)的类型为Int -> Int -> String,但是它不会显示输入类型与列表中的元素匹配的类型(Int, Int) -> String

重写过程可以分解;假设f = formatString ("Value: " ...)

map (\(x,y) -> f x y)  ≡  map (\(x,y) -> uncurry f (x,y))  ≡  map (uncurry f)

也就是说,首先你发现f来实现接受元组的函数,然后你执行常规Eta-conversion,因为\(x,y) -> uncurry f (x,y)等同于uncurry f。要打印结果中的每一行,请使用mapM_

mapM_ (putStrLn . uncurry $ formatToString ...) [(1,100), (2,350), ...]

如果你运行 hlint YourFile.hs ,我们会推荐这些重写。