gpt4 book ai didi

Haskell printf 参数作为数组

转载 作者:行者123 更新时间:2023-12-04 10:04:26 24 4
gpt4 key购买 nike

我想用数组调用 Text.Printf 函数 printf 但我找不到方法。
这是两个不起作用的版本(实际上是相同的想法)。

import Text.Printf

printfa :: (PrintfArg a) => String -> [a] -> String
printfa format args = step (printf format) args
where
step :: (PrintfType r, PrintfArg a) => r -> [a] -> r
step res (x:[]) = res x
step res (x:xs) = step (res x) xs

printfa' :: (PrintfArg a) => String -> [a] -> String
printfa' format args = foldr (\arg p -> p arg) (printf format) args

main = putStrLn $ printfa "%s %s" ["Hello", "World"]

GHC 错误是:
printfa.hs:8:23:
Couldn't match type `r' with `a1 -> r'
`r' is a rigid type variable bound by
the type signature for
step :: (PrintfType r, PrintfArg a1) => r -> [a1] -> r
at printfa.hs:8:5
The function `res' is applied to one argument,
but its type `r' has none
In the expression: res x
In an equation for `step': step res (x : []) = res x

printfa.hs:12:41:
The function `p' is applied to one argument,
but its type `String' has none
In the expression: p arg
In the first argument of `foldr', namely `(\ arg p -> p arg)'
In the expression: foldr (\ arg p -> p arg) (printf format) args

(原因:我正在编写 DSL 并希望提供 printf 功能。)

最佳答案

首先,意识到 PrintfArg a => [a]不是异类列表。也就是说,即使 IntString都是 PrintfArg 的实例, [ 1 :: Int, "foo" ]不是一个有效的构造。

所以如果你定义了一个函数 :: PrintfArg a => String -> [a] -> String ,所有的参数都将被限制为同一类型。

为了解决这个问题,您可以使用存在量化。

{-# LANGUAGE ExistentialQuantification #-}
import Text.Printf

data PrintfArgT = forall a. PrintfArg a => P a

printfa :: PrintfType t => String -> [ PrintfArgT ] -> t
printfa format = printfa' format . reverse
where printfa' :: PrintfType t => String -> [ PrintfArgT ] -> t
printfa' format [] = printf format
printfa' format (P a:as) = printfa' format as a

main = do
printfa "hello world\n" []
printfa "%s %s\n" [ P "two", P "strings"]
printfa "%d %d %d\n" (map P $ [1 :: Int, 2, 3])
printfa "%d %s\n" [ P (1 :: Int), P "is the loneliest number" ]

您的第一个解决方案不起作用的原因是您通过了 res一步作为论据。

当你有 foo :: Constraint a => a -> t您保证 foo 将适用于 Constraint 的所有实例.尽管存在 PrintfType 的实例它可以带参数,但并非所有实例都可以。因此你的编译器错误。

相反,当您有 foo :: Constraint a => t -> a ,您保证 foo 将返回 Constraint 的任何所需实例.同样,调用者可以选择哪个实例。这就是我的代码有效的原因 - 当 printfa'递归,它需要递归调用从 (PrintfArg a, PrintfType t) => a -> t 返回一个值实例。

对于您的第二次尝试,编译器会提示因为 foldr要求迭代之间的累加值类型相同。 GHC 注意到累加值必须是函数类型 (PrintfArg a, PrintfType t) => a -> t ,因为您在迭代函数中应用了它。但是您返回应用的值,它可以确定其类型为 t .这意味着 t等于 a -> t ,这是 GHC 不喜欢的,因为它不允许无限类型。所以它提示。

如果你想使用折叠,你可以,你只需要使用 Rank2Types 屏蔽累加器类型或 RankNTypes在迭代之间保持类型不变。
{-# LANGUAGE ExistentialQuantification #-}
{-# LANGUAGE RankNTypes #-}
import Text.Printf

data PrintfArgT = forall a. PrintfArg a => P a
data PrintfTypeT = T { unT :: forall r. PrintfType r => r }

printfa :: PrintfType t => String -> [ PrintfArgT ] -> t
printfa format = unT . foldl (\(T r) (P a) -> T $ r a ) (T $ printf format)

关于Haskell printf 参数作为数组,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18243480/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com