如何组合等序元素(功能编程)?

时间:2018-11-10 22:58:56

标签: functional-programming f# ocaml sml

我想编写一个函数,该函数接受序列<1,1,2,2,3>并返回具有相等元素的序列,这些元素的分组方式类似于<< 1,1>,<2,2>,<3> >。

我使用的是序列,而不是列表,但是其中一些功能是相似的。我正在考虑使用的某些功能是映射,缩小,制表,过滤,附加等。

Reduce接受一个关联函数,并返回该运算符“减少”的序列。因此,降低op + 0 <1,2,3> = 6。

我的第一个想法是使用map将序列提升一个级别。 因此,<1,1,2,2,3> => << 1>,<1>,<2>,<2>,<3 >>。

然后,我正在考虑使用reduce,在该方法中,我创建了一个函数,该函数接受像(x,y)这样的成对元素。如果x == y,则返回其他值,则不执行任何操作。但是...这并不完全有效,因为函数在两种情况下都必须返回相同类型的东西。

有人可以给我一些正确使用方法的提示,例如我可以使用的更高阶函数吗?我正在使用SML,但我并没有要求任何人给我一个完整的答案,因此(使用任何一种功能性语言坦诚地说)任何高级技巧都将不胜感激

4 个答案:

答案 0 :(得分:3)

我猜您所指的reduce函数与F#中的fold函数相同:

val fold : ('State -> 'Value -> 'State) -> 'State -> 'Value list -> 'State

这将获取一个值列表,以及一个初始状态和一个在迭代列表值时转换状态的函数。

您可以一次完成自己想做的事情。您需要保持一些状态。假设您在1,1,2,2,3的中间(例如,在第二个2上)。现在您需要:

  • 您当前正在收集的值-2
  • 包含当前收集值的值列表-[2](序列中的第一个2
  • 您先前收集的值列表的列表-[ [1; 1] ]

您将从初始状态-1, [], []开始(使用-1作为输入中不会出现的某些值)。然后,您需要编写根据当前值转换状态的函数。这需要处理几种情况:

  • 如果该值与您一直在收集的值不同,则需要将收集到的值列表添加到列表列表中(除非它为空)
  • 当值相同时,您需要将其添加到现在收集的值列表中并继续

希望这能为您提供足够的信息,以找出如何执行此操作,而无需实际透露完整的源代码!

答案 1 :(得分:2)

如果F#是您的语言,只需使用Seq.groupBy function

input |> Seq.groupBy id |> Seq.map snd

否则,

我假设您的语言支持Seq.distinctSeq.foldSeq.mapSeq.init。这些功能的F#版本可以在此document中找到。

然后您可以执行以下步骤:

1)使用Seq.distinct对输入序列进行不同的序列:

input |> Seq.distinct

2)编写一个函数,该函数通过使用Seq.fold计算一个序列中某个值出现的次数:

let count x theSeq =
    theSeq
    |> Seq.fold (fun n e -> if x = e then n+1 else n) 0

3)使用count函数用输入序列中出现的次数来装饰不同序列的每个元素:

Seq.map (fun x -> (x, count x input))

4)最后,使用Seq.init复制相等的元素:

Seq.map (fun (x, count) -> Seq.init count (fun i -> x))

整个代码:

let count x theSeq =
    theSeq
    |> Seq.fold (fun n e -> if x = e then n+1 else n) 0

input
|> Seq.distinct
|> Seq.map (fun x -> (x, count x input))
|> Seq.map (fun (x, count) -> Seq.init count (fun i -> x))

答案 2 :(得分:0)

不知道您使用的语言,但是从功能角度来看,我会:

  1. 采用不同值的顺序
  2. 将不同序列中的每个值映射到过滤后的原始序列。

伪代码:

originalSequence = <1,1,2,2,3>
distinctSequence = originalSequnce.distinct() // <1,2,3>
result = distinctSequence.map(elem => originalSequence.filter(e == elem)) // <<1,1>, <2, 2>, <3>>

答案 3 :(得分:0)

在Haskell中,您有groupgroupBy。它们由名为span的辅助函数制成。不幸的是,标准ML没有这么丰富的标准库,因此您必须自己创建函数。但是您可以使用相同的方法:

  1. 定义一个函数span,该函数将列表分成两部分,第一部分是某些谓词为真的最长前缀,第二部分是列表的其余部分。

    fun span p [] = ...
      | span p (x::xs) = ... if p x then ... else ...
    

    例如,

    - span (fn n => n <= 3) [2,3,4,1,5]
    > val it = ([2,3], [4,1,5])
    

    这有点困难,因为您必须以某种方式将x添加到递归调用span p xs的结果中,即使它返回一对列表也是如此;所以你不能只写x :: span p xs;您必须解开返回的货币对并返回(x :: ys, zs)(ys, x :: zs)(一旦p x为假,就停止递归)。

  2. 定义使用groupBy的函数spangroupBy使用的函数应该有两个参数,与span接受一个参数的p不同:第一个是要分组的元素,第二个是后续元素。 / p>

    fun groupBy f [] = ...
      | groupBy f (x::xs) = ... use span, f, x and xs to create (ys, zs) ...
                            ... ys is the first group ...
                            ... groupBy f zs is the remaining groups ...
    

    如果函数f处于咖喱状态(即类型为

    ),则会有所帮助
    'a -> 'a -> bool
    

    因为它可以像val (ys, zs) = span (f x) xs那样使用。

如果您想使用此方法,请随时提出后续问题。