我有一个函数女巫以强制性的风格编写,无法理解如何将其转换为更强大的功能方法。
该函数接受一系列字符串并返回一个seq的元组,其中每个元组由输入中的2,7,12,..和5,10,15,..项组成。
示例:
输入= {“Lorem”,“ipsum”,“dolor”,“set”,“amet”,“consectetuer”,“adipiscing”,“elit”,“Aenean”,“commodo”,“ligula”, “eget”,“dolor”,“Aenean”,“massa”}
Ouput = {(“ipsum”,“amet”),(“adipiscing”,“commodo”),(“eget”,“massa”)}
let convert (input : seq<string>) : seq<(string * string)> =
let enum = input.GetEnumerator()
let index = ref 0
let first = ref ""
let second = ref ""
seq {
while enum.MoveNext() do
let modIndex = !index % 5
index := !index + 1
if (modIndex % 2 = 0 && !first = "") then first := enum.Current
if (modIndex % 5 = 0 && !second = "") then second := enum.Current
if modIndex = 0 then
let result = (!first, !second)
first := ""
second := ""
yield result
}
对于起点的任何帮助或提示表示赞赏。
答案 0 :(得分:6)
我不完全理解你想要的行为 - 生成你想要配对的索引的算法是什么?无论如何,一个很好的功能解决方案是将你想要分开的元素分开,然后使用Seq.zip
组合它们。
您可以使用Seq.mapi
向值添加索引,然后使用Seq.choose
获取具有正确索引的值(并跳过所有其他值)。对于硬编码索引,您可以编写如下内容:
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))
我仍然没有看到生成第一个元素的一般机制!
编辑还有一个想法 - 是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)
答案 1 :(得分:1)
这是更惯用的方式。实际上,这是一个班轮;我只是将它对齐以提高可读性。
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
总体思路基于三个步骤:
List
元组,取第2和第5个字符串。 警告如果原始数据长度不是5的乘数,则尾随元素将丢失。第一行是最难的一行。
让我们定义状态。它将是序列号的三元,string option
包含字符串## 2,7等,以及一旦我们遇到元素就添加的“外部”(string*string) list
# #5,10等
该函数将第2个,第7个等元素放置到“内部”string option
,或者,如果i
等于5,10等,则形成元组并将其添加到“外部”List
(为了清晰起见,删除内部值)。
我们使用List.fold
,因此最终列表将被颠倒。
初始状态是MSDN中(0, None, []). More info on
List.fold`的三元。
第二行只是从三元组中获取第三个元素。我已经使它成为允许链绑定的功能。
由于List
运算符的性质,第三行会反转::
。
根据初始列表的长度。如果它找到了“2nd”元素但未达到“5th”,则 triple 的第二个元素具有该值。您可以通过验证来检测错误情况:
...
|> fun (_, temp, elem) ->
if temp.IsSome
then failwith "Data length must be a multiplier of 5"
else elem
...
这是一个支持两个以上元素的更长的代码:
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
)
请注意,此变体在第一次调用时不会删除元素,因此您可以检索两个以上的项目。
重要:列表按相反的顺序处理,因此项目索引是从输入结束时计算出来的。您可以将其更改为List.fold
的费用,或者进一步按Output1
中的顺序撤消列表。
由于<|
的签名,请注意反向绑定运算符List.foldBack
。
您可以通过类似的方式检查错误:通过测试“内部”列表是否为空。
答案 2 :(得分:0)
我来自haskell而不是f#所以我会给出一个可能无效的f#代码的想法:
首先,我会从输入中生成两个列表:
let zeromod5 = filter (index == 0 % 5) input
let twomod5 = filter (index == 2 % 5) input
应该产生列表
{ "ipsum", "adipiscing","eget"}
{ "amet", "commodo","massa" }
然后压缩它们,我。即通过
之类的东西制作一对配对列表zip zeromod5 twomod5
Haskell版本:
zipWeird :: [String] -> [(String, String)]
zipWeird ss = zip twoMod5s zeroMod5s
where 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
where zSS = zip (tail ss) (drop 4 ss)
ezSS = zip zSS [1..]
input :: [String]
input = words ("Lorem ipsum dolor sit amet, consetetur sadipscing elitr, "++
"sed diam nonumy eirmod tempor invidunt ut labore et dolore "++
"magna aliquyam erat, sed diam voluptua. At vero eos et "++
"accusam et justo duo dolores et ea rebum. Stet clita kasd "++
"gubergren, no sea takimata sanctus est Lorem ipsum dolor sit "++
"amet.")
main :: IO ()
main = do
print $ zipWeird input
print $ zipWeird2 input