【问题标题】:How to combine equal sequence elements (functional programming)?如何组合等序元素(函数式编程)?
【发布时间】:2019-04-14 02:47:48
【问题描述】:

我想编写一个函数,它接收一个序列 并返回具有相同元素的序列,如 、、 >。

我使用的是序列,而不是列表,但其中一些功能是相似的。我正在考虑使用的一些功能是 map、reduce、tabulate、filter、append 等。

Reduce 接受一个关联函数并返回由该运算符“减少”的序列。所以,减少 op+ 0 = 6。

我的第一个想法是使用 map 将序列提高一个级别。 所以, => ,,,,>。

然后,我正在考虑使用 reduce,我在其中创建了一个函数,该函数采用 (x,y) 之类的元素对。如果 x == y,那么我返回 else,我什么也不做。但是......这并不完全有效,因为函数在两种情况下都必须返回相同类型的东西。

有人能给我一些关于正确路径的提示吗,比如我可以使用哪些高阶函数?我正在使用 SML,但我并没有要求任何人给我一个完整的答案,所以任何高级技巧都将不胜感激(老实说,在任何功能语言中)

【问题讨论】:

  • 不了解您正在使用的语言,但从功能的角度来看,我会:1)获取不同值的序列 2)将不同序列中的每个值映射到过滤后的原始序列。

标签: functional-programming f# ocaml sml


【解决方案1】:

如果您的语言是 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】:

    在 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 为 false 时停止递归)。

    2. 定义一个使用span 的函数groupBygroupBy 使用的函数应该有两个参数,而不像 span 中的 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一样使用。

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

    【讨论】:

      【解决方案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 作为不会出现在您的输入中的某个值)。然后,您需要编写根据当前值转换状态的函数。这需要处理几种情况:

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

      希望这可以为您提供足够的信息来弄清楚如何做到这一点,而无需真正透露完整的源代码!

      【讨论】:

        【解决方案4】:

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

        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>>
        

        【讨论】:

          猜你喜欢
          • 2019-04-14
          • 2017-11-24
          • 1970-01-01
          • 1970-01-01
          • 2021-01-16
          • 1970-01-01
          • 2018-06-15
          • 2018-03-13
          • 2023-04-09
          相关资源
          最近更新 更多