24

在我的 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 的两个参数?

4

3 回答 3

47

函数uncurry将两个参数(咖喱)函数转换为对函数。这是它的类型签名:

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)]
于 2011-02-25T14:01:49.347 回答
3
mapM_ (\(x,y) -> printf "Value: %d %d\n" x y) [(1,100),(2,350),(3,600),(4,200)]
于 2011-02-25T14:06:29.507 回答
2

一个类型安全的替代方案Text.Printf格式化包。Text.Printf.printf不确保在编译时格式化参数的数量与参数的数量及其类型一致。阅读 Chris Done 的文章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)有 type Int -> Int -> String,但 uncurrying 它给出了(Int, Int) -> String输入类型与列表中的元素匹配的类型。

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

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

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

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

如果您运行hlint YourFile.hs,则会向您推荐这些重写。

于 2015-09-29T15:59:56.163 回答