提问人:mistahwhite 提问时间:4/26/2023 更新时间:4/26/2023 访问量:117
如何在 Haskell 中编写一个函数来打印出任何大小的元组的每个元素?
How can I write a function in Haskell that prints out every element of a tuple of any size?
问:
我知道我可以做这样的事情
listy = zip [1,2,3] [a,b,c]
testy = listy !! 0 --(1,"a")
let (x,y) = testy
putStr((show x) ++ " " ++ y ++ "\n")
获得 1 A
但是,假设我有一堆元组,例如
("John","Cena")
("Chicken","Egg","Parmesan","Cream","Noodles","Milk")
("Beans","Rice","Meat")
("Tomato","Basil","IceCream","Midi")
我怎样才能编写一个适用于任何长度的元组的函数,并像我上面一样将它们打印出来?
答:
你不能,每个不同长度的元组都是不同的类型,但 Haskell 是强类型的,所以一个函数只能接受单一类型的参数。
你所有的例子都应该是列表。
如果您只想打印元组的每个元素,则元组类型具有执行该 1 的实例,因此将打印出元组的每个元素。但是,它将在 Haskell 语法中这样做:Show
print
λ print (1, "foo", 2, "bar")
(1,"foo",2,"bar")
如果你只是想看看元组中有哪些值,那么我建议你简单地习惯这种格式(而不是你的例子似乎建议的空格分隔的无引号格式)。没有什么比 just 或 更简单了。print
show
问题在于,元组类型并不像列表那样是真正的集合。阿拉伯数字
在列表中,所有元素都属于同一类型,因此很容易将函数映射到它们上(如果给定函数可以处理列表中的任何元素,则可以处理所有元素)。不同长度(但元素类型相同)的列表都是同一类型的成员,因此一个函数可以处理任何长度的列表(将元素组合成单个结果,例如空格分隔的字符串)。
但是,在元组中,所有项的类型都是独立的。如果我有一个通用的未知元组,那么我不可能在所有元素上映射的函数,因为我可能想要使用的任何函数都将处理特定类型,并且元组元素都可以是不同的类型。因此,例如,没有简单的方法可以将未知元组的所有元素转换为字符串。(a, b, c, d ...)
这个问题已经在你自己的原始示例中出现过,打印元组,如 .你必须说将第一个字段转换为字符串,但随后只是用于“处理”第二个字段;你使用不同的代码来处理每个字段,所以你实际上甚至不想对所有字段做“同样的事情”,即使只是在这个小例子中。(1, "a")
show x
y
此外,不同长度的元组也是不同的类型,这些类型彼此之间没有有用的关系,因此它们不能全部由同一函数处理。即使我们假设我们可以将所有元组元素转换为 ,接受 2 元组字符串并组合其结果的函数将具有 类型,而接受 3 元组的函数将具有 类型,依此类推。无法将所有这些函数类型统一为可以处理任何元组的单一类型。由于没有办法表达这种类型,我们不可能编写一个可以做到这一点的函数。String
(String, String) -> String
(String, String, String) -> String
{{some-kind-of-tuple}} -> String
实际上,各种元组类型 、 、 等实际上彼此之间并不相关,并且不应该像列表、数组、集合等那样一起被视为“集合”。元组更像是一种“匿名数据类型”;元组语法只是特殊的速记语法,因此我们不必定义以下内容:(a, b)
(a, b, c)
(a, b, c, d)
data TwoItems a b = TwoItems a b
data ThreeItems a b c = ThreeItems a b c
data ElevenItems a b c d e f g h i j k = ElevenItems a b c d e f g h i j k
<etc>
如果我们手动定义它们,则每个数据类型都将是一个单独的数据类型,无法使用处理 .也没有任何自动方法可以将函数应用于 ;它里面的东西是数据类型的字段,而不是集合的元素。ThreeItems
ElevenItems
ElevenItems
元组就是这样。我们只是有特殊的语法,以便更容易地使用这些“无聊”的数据类型,这些数据类型什么都不做,但包含一些任何独立类型的字段。
如果我们声明了一堆单独的数据类型,并希望能够将它们中的任何一个转换为字符串,我们需要为每种类型创建一个单独的函数,说明如何为该类型执行此操作;无法编写处理任何这些数据类型的单个函数。
我们可以使用类型类将所有这些单独的函数“合并”到一个可以被赋予单个名称的东西中,但这与为任何其他类型创建类型类实例的工作方式完全相同;它不会利用不同元组类型之间的任何共性(因为没有任何共性)。您仍然需要以声明的形式为每种类型编写所有单独的函数。为了将内容转换为字符串,这已经以类和实例的形式为您完成,但如果您不喜欢它使用的格式,那将无济于事;您需要单独处理所有这些元组类型,然后可以选择使用类型类来统一定义。instance
Show
但实际上,你想要一个具有未知数值的程序,并希望对所有值“相同”,这一事实几乎总是表明你应该用来存储它们的类型是列表(或其他集合类型),而不是元组。实际上,您应该对代码进行的更改更有可能是“首先不要将这些值存储在元组的字段中”,而不是“编写一堆单独的函数来打印不同长度的元组”。
1 从技术上讲,尺寸仅为 15。然而,比这更大的元组是相当罕见的。
2 如果你熟悉 Python,你可能习惯于将元组视为与列表非常相似;如果是这样,我建议有意识地努力在 Haskell 中训练自己,因为它使用列表和元组之间的区别与 Python 使用它调用列表和元组的类型之间的区别截然不同。
评论