【问题标题】:F# - Breaking a List into Concatenated Strings by an IntervalF# - 按间隔将列表分解为串联字符串
【发布时间】:2010-10-01 07:07:08
【问题描述】:

我有一个电子邮件地址列表,我需要向每个地址发送电子邮件通知。我想一次在 25 个地址块中执行此操作。在像 F# 这样的函数式语言中有什么快速的方法可以将 25 个电子邮件地址“折叠”(连接)在一起……每个电子邮件地址用分号分隔。我知道 .NET 中有 String.Split 方法,但我需要一次连接 25 个。

在 F# 中执行此操作的最优雅的方式是什么?

【问题讨论】:

  • 我可能需要一些时间来消化这些,我还在纠结于 F#。我想知道在 C# 或 F# 中执行此操作的最简单方法是:1) 将所有电子邮件连接到一个大字符串中。但每 25 封电子邮件使用 ;;代替 ;作为分隔符 2) 然后使用 String.Split ;;想法?

标签: .net f# list concatenation fold


【解决方案1】:

如果这仍然相关,这是一个使用分隔符连接字符串序列但不转换为数组的函数。

open System
open System.Text

/// Join a sequence of strings using a delimiter.
/// Equivalent to String.Join() but without arrays.
let join (items : seq<string>) (delim : string) =
    // Collect the result in the string builder buffer
    // The end-sequence will be "item1,delim,...itemN,delim"
    let buff = 
        Seq.fold 
            (fun (buff :StringBuilder) (s:string) -> buff.Append(s).Append(delim)) 
            (new StringBuilder()) 
            items

    // We don't want the last delim in the result buffer, remove
    buff.Remove(buff.Length-delim.Length, delim.Length).ToString()

【讨论】:

    【解决方案2】:

    Seq 模块有一个窗口函数,但它给你一个滑动窗口,所以对这个问题没有好处。

    这是我的版本,我没有测试性能与其他答案的比较,但它更漂亮(我认为!)

    //Add an "extension function" to the Seq module
    module Seq = 
        let rec chunks n (s:#seq<_>) =
            seq {
                     if Seq.length s <= n then
                        yield s
                     else
                        yield Seq.take n s
                        yield! chunks n (Seq.skip n s)           
                }
    
    //Make curried version of String.Join
    let join sep (s:#seq<string>) = System.String.Join(sep, Array.of_seq s)
    
    //Compose the bits together
    let mailMerge n = Seq.chunks n >> Seq.map (join ";")
    
    //Test
    mailList |> mailMerge 25
    

    你也可以看看Array slices

    【讨论】:

      【解决方案3】:

      这是一种分成最多 N 个组的方法:

      // break a sequence up into a sequence of arrays, 
      // each of length at most 'n'
      let Break n (s:seq<_>) =
          seq {
              use e = s.GetEnumerator()
              while e.MoveNext() do
                  let i = ref 0
                  yield [|
                      yield e.Current
                      i := !i + 1
                      while !i < n && e.MoveNext() do            
                          yield e.Current
                          i := !i + 1 |] }
      

      然后你的解决方案就像

      let Sendmail addr = printf "%A\n\n" addr
      let allMails = [for i in 1..25 -> sprintf "a%i@example.com" i]
      allMails 
      |> Break 5
      |> Seq.map (fun arr -> System.String.Join(";", arr))
      |> Seq.iter Sendmail
      

      【讨论】:

        【解决方案4】:

        它并不过分漂亮,但它确实有效:

        let breakWords separator groupSize (input : string) =
            let words = input.Split([| separator |])
            let arraySize = words.Length / groupSize + (if words.Length % groupSize > 0 then 1 else 0)
            let groups = Array.init arraySize (fun index ->
                let startIndex = index * groupSize
                let endIndex = Math.Min(startIndex + groupSize - 1, words.Length - 1)
                words.[startIndex .. endIndex])
            groups |> Seq.map (fun x -> String.Join(";", x))
        

        【讨论】:

          猜你喜欢
          • 2010-10-19
          • 2011-03-18
          • 2011-06-09
          • 1970-01-01
          • 2020-05-31
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多