需要想法将F#命令式代码转换为功能性代码

需要想法将F#命令式代码转换为功能性代码,f#,functional-programming,imperative,F#,Functional Programming,Imperative,我有一个函数,它是用命令式编写的,我不知道如何将它转换成更健壮的函数方法 该函数获取一个字符串序列并返回一个元组序列,其中每个元组由2,7,12,。。和5,10,15,。。输入中的项目 例如: 输入={“Lorem”、“ipsum”、“dolor”、“set”、“amet”、“Concertetuer”、“Adipising”、“elit”、“Aenean”、“commodo”、“ligula”、“eget”、“dolor”、“Aenean”、“massa”} 输出={(“ipsum”、“ame

我有一个函数,它是用命令式编写的,我不知道如何将它转换成更健壮的函数方法

该函数获取一个字符串序列并返回一个元组序列,其中每个元组由2,7,12,。。和5,10,15,。。输入中的项目

例如:

输入={“Lorem”、“ipsum”、“dolor”、“set”、“amet”、“Concertetuer”、“Adipising”、“elit”、“Aenean”、“commodo”、“ligula”、“eget”、“dolor”、“Aenean”、“massa”}

输出={(“ipsum”、“amet”)、(“adipising”、“commodo”)、(“eget”、“massa”)}

let convert(输入:seq):seq=
让enum=input.GetEnumerator()
设索引=ref 0
让第一个=参考“”
设秒=ref“”
序号{
当enum.MoveNext()执行以下操作时
让modIndex=!索引%5
索引:=!索引+1
如果(modIndex%2=0&&!first=”“),则first:=enum.Current
如果(modIndex%5=0&&!second=”“),则second:=enum.Current
如果modIndex=0,则
让结果=(!first,!second)
第一:=“”
第二名:=“”
产量结果
}

任何关于起点的帮助或提示都将不胜感激。

我不完全理解您想要的行为-生成您想要配对的索引的算法是什么?无论如何,一个很好的功能性解决方案是分别获取要配对的元素,然后使用
Seq.zip
组合它们

您可以使用
Seq.mapi
将索引添加到值中,然后使用
Seq.选择
获取具有正确索引的值(并跳过所有其他值)。对于硬编码索引,您可以编写如下内容:

let indexed = input |> Seq.mapi (fun i s -> i, s)
Seq.zip 
  (indexed |> Seq.choose (fun (i, v) -> if i=1 || i=6 || i=11 then Some v else None))
  (indexed |> Seq.choose (fun (i, v) -> if i=4 || i=9 || i=14 then Some v else None))
我使用了你的数字-1,因为指数是从0开始的-所以上面给出了你想要的结果。第二个系列看起来是5的倍数,因此您可能希望
i%5=4
生成第二个元素:

let indexed = input |> Seq.mapi (fun i s -> i, s)
Seq.zip 
  (indexed |> Seq.choose (fun (i, v) -> if i=1 || i=6 || i=11 then Some v else None))
  (indexed |> Seq.choose (fun (i, v) -> if i%5 = 4 then Some v else None))
let Output2 = 
    Input
    |> List.foldBack
        (fun x (i, l1, l2) ->
            if i = 4
            then 0, [], (x::l1)::l2
            else i+1, x::l1, l2
        )
        <| (0, [], [])
    |> fun (_, _, elem) -> elem
    |> List.choose
        (function
        | [_; first; _; _; second] -> Some (first, second)
        | _-> None
        )
我仍然没有看到生成第一个元素的一般机制

编辑还有一个想法-第一个序列是由
i*5+2
生成的,第二个序列是由
i*5
生成的吗?在这种情况下,您的示例是错误的,但您可以这样写:

let indexed = input |> Seq.mapi (fun i s -> i, s)
Seq.zip 
  (indexed |> Seq.choose (fun (i, v) -> if i%5 = 2 then Some v else None))
  (indexed |> Seq.choose (fun (i, v) -> if i%5 = 0 then Some v else None))
。。。或者,如果您想使代码更具可读性,您可以重构:

let filterNthElements div rem = 
  input |> Seq.mapi (fun i s -> i, s)
        |> Seq.choose (fun (i, v) -> if i%div = rem then Some v else None)

Seq.zip (filterNthElements 5 2) (filterNthElements 5 0)
我来自haskell而不是f#,因此我将给出一个可能无效的f#代码想法:

首先,我将根据输入生成两个列表:

let zeromod5 = filter (index == 0 % 5) input
let twomod5 = filter (index == 2 % 5) input
哪一个应该产生列表

{ "ipsum", "adipiscing","eget"}
{ "amet", "commodo","massa" }
然后拉上拉链。E按以下方式列出一个配对列表

zip zeromod5 twomod5
编辑: Haskell版本:

zipWeird::[String]->[(String,String)]
zipWeird ss=zip twoMod5s zeroMod5s
其中zeroMod5s=map fst$filter(\(\,y)->y`mod`5==0)eSS
twoMod5s=map fst$filter(\(\,y)->y`mod`5==2)eSS
eSS=zip ss[1..]
zipWeird2::[String]->[(String,String)]
zipWeird2 ss=map fst$filter(\(\,y)->y`mod`5==1)ezSS
其中zSS=zip(尾部ss)(下降4 ss)
ezSS=zip zSS[1..]
输入::[字符串]
输入=文字(“Lorem ipsum door sit amet,consetetur sadipscing eliter,”++
“暂时不受劳动和福利的影响”++
“在vero eos等处的大面积、直径和体积”++
“两个多洛雷斯和一个重罪的受害者”++
“古伯格伦,没有大海,塔基马塔圣所是一座美丽的圣殿”++
“阿梅特。”)
main::IO()
main=do
打印$zipWeird输入
打印$zipWeird2输入

这里有一种更惯用的方法。事实上,这是一个班轮;为了更好的可读性,我刚刚调整了它

let Input = [ "Lorem"; "ipsum"; "dolor"; "set"; "amet"; "consectetuer";
              "adipiscing"; "elit"; "Aenean"; "commodo"; "ligula"; "eget";
              "dolor"; "Aenean"; "massa" ]

// Short solution that does not support more than two values
let Output1 =
    Input
    |> List.fold
        (fun (i, l1, l2) x ->
            if i=4 then 0, None, (l1.Value, x)::l2
            elif i=1 then i+1, Some x, l2
            else i+1, l1, l2
        )
        (0, None, [])
    |> fun (_, _, elem) -> elem
    |> List.rev
主意 总体思路基于三个步骤:

  • 将列表拆分为元组的
    列表
    ,使用第二个和第五个字符串警告如果原始数据长度不是5的倍数,则尾部元素将丢失
  • 通过使用第三个元素从三元组中过滤出临时数据,这是我们的主要目标
  • 颠倒清单

  • 解释 第一行是最难的

    让我们来定义我们的状态。它将是一个三元组的序列号,一个包含字符串###2、7等的
    字符串选项
    ,以及一个“外部”
    (字符串*字符串)列表
    ,在我们遇到元素##5、10等后添加

    该函数将把第2、第7等元素放入“内部”
    字符串选项
    ,或者,如果
    i
    等于5、10等,则形成一个元组并将其添加到“外部”
    列表
    (为了清晰起见,删除内部值)

    我们使用
    List.fold
    ,因此最终的列表是反向的

    初始状态是
    的三元组(0,无,[])。有关列表的详细信息。请折叠

    第二行只取三元组的第三个元素。我已经把它变成了一个允许链绑定的函数

    由于
    运算符的性质,第三行反转
    列表

    按照初始列表的长度。如果已找到“第二个”元素但未到达“第五个”,则三元组的第二个元素具有该值。您可以通过验证来检测错误情况:

    ...
    |> fun (_, temp, elem) ->
        if temp.IsSome
        then failwith "Data length must be a multiplier of 5"
        else elem
    ...
    

    下面是一段较长的代码,它支持两个以上的元素:

    let indexed = input |> Seq.mapi (fun i s -> i, s)
    Seq.zip 
      (indexed |> Seq.choose (fun (i, v) -> if i=1 || i=6 || i=11 then Some v else None))
      (indexed |> Seq.choose (fun (i, v) -> if i%5 = 4 then Some v else None))
    
    let Output2 = 
        Input
        |> List.foldBack
            (fun x (i, l1, l2) ->
                if i = 4
                then 0, [], (x::l1)::l2
                else i+1, x::l1, l2
            )
            <| (0, [], [])
        |> fun (_, _, elem) -> elem
        |> List.choose
            (function
            | [_; first; _; _; second] -> Some (first, second)
            | _-> None
            )
    
    let Output2=
    输入
    |>List.foldBack
    (乐趣x(i、l1、l2)->
    如果i=4
    然后0,[],(x::l1)::l2
    否则i+1,x::l1,l2
    )
    乐趣(元素)->elem
    |>选择
    (功能
    |[\u;第一;\u;\ u;\ u;第二]->一些(第一,第二)
    |无
    )
    
    注意:此变体在第一次调用期间不会删除元素,因此您可以检索两个以上的项

    重要:列表按相反顺序处理,因此项目索引从输入结束时开始计算。你可以改变我