Корректировать элементы кортежа функции в качестве аргументов в Haskell?
В моей программе 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?
Ответы
Ответ 1
Функция 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)]
Ответ 2
mapM_ (\(x,y) -> printf "Value: %d %d\n" x y) [(1,100),(2,350),(3,600),(4,200)]
Ответ 3
Альтернативой Text.Printf
, альтернативной типу, является пакет formatting. 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)
имеет тип 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)
То есть, сначала вы не выполняете функцию, которая принимает кортежи, а затем вы выполняете обычное Eta-conversion, поскольку \(x,y) -> uncurry f (x,y)
эквивалентно просто uncurry f
. Чтобы напечатать каждую строку в результате, используйте mapM_
:
mapM_ (putStrLn . uncurry $ formatToString ...) [(1,100), (2,350), ...]
Если вы запустите hlint YourFile.hs, эти перезаписи вам будут рекомендованы.