如何在SML中将第一个列表中的元素与第二个列表中的所有元素配对?

时间:2018-10-02 00:44:36

标签: list functional-programming sml smlnj

问题陈述:编写一个函数对,该函数对包含两个整数列表并生成一个成对的列表,其中每个对都是每个列表中每个元素的组合。

例如,pair ([1,2], [3,4,5])应该返回

[(1,3), (1,4), (1,5), (2,3), (2,4), (2,5)].

到目前为止我的工作:

-fun pair(a:int list, b:int list) = if null a then nil else if null b then nil else (hd a, hd b)::pair(a, tl b)@pair(tl a, b);

val pair = fn : int list * int list -> (int * int) list

-pair([1,2],[3,4,5]);

val it = [(1,3),(1,4),(1,5),(2,5),(2,4),(2,5),(2,3),(2,4),(2,5)]

我试图跟踪该函数,以找出为什么显示(2,5)(2,4)(2,5),但我仍然看不到它的踪影。

这似乎很简单,但是我似乎无法解决最后的问题。指出为什么在中间添加这些元素的一些帮助会有所帮助。

谢谢。
彼得

2 个答案:

答案 0 :(得分:0)

由于这是一种练习,因此我不会向您展示问题陈述的答案。

您要生成的东西称为两个列表的笛卡尔积。

您当前的方法(格式更好)

fun pair (a, b) =
  if null a then nil else
  if null b then nil else
  (hd a, hd b) :: pair (a, tl b) @ pair (tl a, b);

会产生重复的结果,因为您在hd b中遗漏了pair (a, tl b)而在hd a中遗漏了pair (b, tl a),但是在第二次迭代中,例如pair (a, tl b),将a的每个剩余元素再次处理tl b的第一个元素。

您可以通过一次处理每个元素来避免这种重复工作。我建议您查看函数mapconcat。通用方法是这样的:对于x的每个元素a,为(x,y)的每个元素y生成b。 “对于每个元素”为map。还有

map (fn x => ...something with (x,y)...) a

生成结果的列表,就像您想要的那样。但是,如果您重复使用与map (fn y => ...) b部分相同的...something with (x,y)...方法,您会惊讶地发现concat可以为您提供帮助。

您可以在不使用mapconcat的情况下解决此问题,而不必使用手动递归,但是您可能需要将工作分为两个功能,因为您需要一个功能可以折叠x中的a,并且对于每个x,都在b上折叠一次。函数map包含了这两个函数共有的递归部分,并且仅允许您编写它们没有的东西。

答案 1 :(得分:0)

主要问题是您要遍历两个列表。

如果您查看示例,

pair ([1,2], [3,4,5]) -> [(1,3), (1,4), (1,5), (2,3), (2,4), (2,5)]

您会看到它有两个子列表,

[(1,3), (1,4), (1,5)]
[(2,3), (2,4), (2,5)]

其中第一个由[1,2]的第一个元素和[3,4,5]的每个元素组成的对组成,第二个是[1,2]的第二个元素也与{的每个元素配对{1}}。
请注意,每个子列表包含所有[3,4,5],但仅包含[3,4,5]的一个元素-第一个与[1,2]相同,第二个为pair ([1], [3,4,5])-因此,您只需要递归第一个列表。

您可以创建这样的列表:

  • 如果任何输入列表为空,则结果为空。
  • 否则:
    1. 获取pair ([2], [3,4,5])的第一个元素,并将其与列表中a的每个元素配对(提示:考虑b。)
    2. map的尾部和所有a的尾部进行配对。
    3. 合并1和2的结果。

具有模式匹配:

b

如果将第1步作为单独的函数编写,则可能会有所帮助。