我对此有一个解决方案,还有几个可行但不令人满意的解决方案,但它花费了大量的工作,似乎没有必要的复杂。
我是不是在F#上漏掉了什么?
问题
我有一个数字序列
let nums = seq { 9; 12; 4; 17; 9; 7; 13; }我想用“索引”来修饰每个数字,所以结果是
seq [(9, 0); (12, 1); (4, 2); (17, 3); ...]看起来很简单!
在实践中,输入可以是非常大和不确定的大小。在我的应用程序中,它来自REST服务。
进一步
seq { let mutable i = o; for num in nums do .. }解决方案,while ... do ... 也是如此。
让我们调用函数decorate,类型为(seq<'a> -> seq<'a * int>),因此它将按照以下方式工作:
nums
|> decorate
|> Seq.iter (fun (n,index) -> printfn "%d: %d" index n)制作:
0: 9
1: 12
2: 4
...
6: 13对于列表来说,这是一个微不足道的问题(除了懒惰的计算),但对于序列来说却是一个棘手的问题。
我的解决方案是使用Seq.unfold,如下所示:
let decorate numSeq =
(0,numSeq)
|> Seq.unfold
(fun (count,(nums : int seq)) ->
if Seq.isEmpty nums then
None
else
let result = ((Seq.head nums),count)
let remaining = Seq.tail nums
Some( result, (count+1,remaining)))这符合所有的要求,也是我想出的最好的。
以下是整个解决方案,并提供诊断以显示延迟评估:
let nums =
seq {
// With diagnostic
let getN n =
printfn "get: %d" n
n
getN 9;
getN 12;
getN 4;
getN 17;
getN 9;
getN 7;
getN 13
}
let decorate numSeq =
(0,numSeq)
|> Seq.unfold
(fun (count,(nums : int seq)) ->
if Seq.isEmpty nums then
None
else
let result = ((Seq.head nums),count)
let remaining = Seq.tail nums
printfn "unfold: %A" result
Some( result, (count+1,remaining)))
nums
|> Seq.cache
// To prevent re-computation of the sequence.
// Will be necessary for any solution. This solution required only one.
|> decorate
|> Seq.iter (fun (n,index) -> printfn "ITEM %d: %d" index n)问题:需要做大量的工作才能到达。与(显然)简单的要求相比,它看起来很复杂。
问题:有更简单的解决方案吗?
讨论一些备选方案。
所有工作,但由于所述原因而不能令人满意
// Most likely: Seq.mapFold
// Fails lazy evalation. The final state must be evaluated, even if not used
let decorate numSeq =
numSeq
|> Seq.mapFold
(fun count num ->
let result = (num,count)
printfn "yield: %A" result
(result,(count + 1)))
0
|> fun (nums,finalState) -> nums // And, no, using "_" doesn't fix it!
// 'for' loop, with MUTABLE
// Lazy evaluation works
// Not extensible, as the state 'count' is specific to this problem
let decorate numSeq =
let mutable count = 0
seq {
for num in numSeq do
let result = num,count
printfn "yield: %A" result
yield result;
count <- count+1
}
// 'for' loop, without mutable
// Fails lazy evaluation, and is ugly
let decorate numSeq =
seq {
for index in 0..((Seq.length numSeq) - 1) do
let result = ((Seq.item index numSeq), // Ugly!
index)
printfn "yield: %A" result
yield result
}
// "List" like recursive descent,
// Fails lazy evaluation. Ugly, because we are not meant to use recursion on Sequences
// https://stackoverflow.com/questions/11451727/recursive-functions-for-sequences-in-f
let rec decorate' count (nums : int seq) =
if Seq.isEmpty nums then
Seq.empty
else
let hd = Seq.head nums
let tl = Seq.tail nums
let result = (hd,count)
let tl' = decorate' (count+1) tl
printfn "yield: %A" result
seq { yield result; yield! tl'}
let decorate : (seq<'a> -> seq<'a * int>) = decorate' 0 发布于 2021-09-24 10:52:05
您可以使用Seq.mapi来做您需要的事情。
let nums = seq { 9; 12; 4; 17; 9; 7; 13; }
nums |> Seq.mapi (fun i num -> (num, i)) 这给了(9, 0); (12, 1); etc...
Seq与IEnumerable在C#中“懒惰”的含义相同。
您可以在这里阅读有关Seq.mapi的内容:
https://fsharp.github.io/fsharp-core-docs/reference/fsharp-collections-seqmodule.html#mapi
在这里阅读有关map使用的更多信息:
发布于 2021-09-24 12:16:46
除了Sean的答案中提到的Seq.mapi函数之外,F#还有一个内置的Seq.indexed函数,它用索引来修饰序列。这并不能完全满足您的要求,因为索引成为元组的第一个元素,但取决于您的用例,它可能会起作用:
> let nums = seq { 9; 12; 4; 17; 9; 7; 13; };;
val nums : seq<int>
> Seq.indexed nums;;
val it : seq<int * int> = seq [(0, 9); (1, 12); (2, 4); (3, 17); ...]如果我试图使用一个更原始的函数来实现这一点,可以使用Seq.scan来实现,这有点像折叠,但会产生一个惰性的状态序列。唯一棘手的是,您必须构造初始状态,然后处理序列的其余部分:
Seq.tail nums
|> Seq.scan (fun (prevIndex, _) v -> (prevIndex+1, v)) (0, Seq.head nums)这将不适用于空列表,即使该函数在逻辑上应该能够处理这个问题。
发布于 2021-09-25 17:49:30
for并不坏,或者说是错的。for和yield在seq {}中是如何编写新的seq函数的,如果在Seq模块中提供的任何函数都不是最合适的。使用这种特殊结构既不错误,也不坏。它与C# foreach和yield语法相同.在有限的范围内使用可变的
返回可变值。
seq中,而不是放在外部。你的版本错了。让我们假设
let xs = decorate [3;6;7;12;9]
for x in xs do
printfn "%A" x
for x in xs do
printfn "%A" x现在你有两个版本的装饰。第一个版本
let decorate numSeq =
let mutable count = 0
seq {
for num in numSeq do
yield (num,count)
count <- count + 1
}将印刷:
(3, 0)
(6, 1)
(7, 2)
(12, 3)
(9, 4)
(3, 5)
(6, 6)
(7, 7)
(12, 8)
(9, 9)或者换句话说。每次迭代序列时,都会在所有调用之间共享可变的。作为一般的提示。如果您想返回一个seq,那么将所有代码放入seq中。然后把seq {}放在=标志后面。如果你这样做的话。
let decorate numSeq = seq {
let mutable count = 0
for num in numSeq do
yield (num,count)
count <- count + 1
}得到正确的输出:
(3, 0)
(6, 1)
(7, 2)
(12, 3)
(9, 4)
(3, 0)
(6, 1)
(7, 2)
(12, 3)
(9, 4)此外,您还解释说,这个版本不是“可扩展的”。但是使用mapi的版本选择为“正确”。有同样的问题,它只提供一个索引,仅此而已。
如果您想要一个更通用的版本,您总是可以创建一个函数,它的值是函数参数。例如,您可以将上述函数更改为此代码。
let decorate2 f (state:'State) (xs:'T seq) = seq {
let mutable state = state
for x in xs do
yield state, x
let newState = f state x
state <- newState
}现在,decorate2需要一个可以自由传递的状态,以及一个更改状态的函数。有了这个函数,您就可以编写:
decorate2 (fun state _ -> state+1) 0 [3;6;7;12;9]函数签名与Seq.scan几乎相同,但仍然有点不同。但是,如果您想要创建一个indexed函数,可以像这样使用scan。
let indexed xs =
Seq.scan (fun (count,_) x -> (count+1,x)) (0,Seq.head xs) (Seq.skip 1 xs)在我看来。与decorate或decorate2相比,这个版本更难理解、更难理解,也更难理解。
还有一张纸条。标准库中已经有一个Seq.indexed函数,它可以做您想做的事情。
for x in Seq.indexed [3;6;7;12;9] do
printfn "%A" x将打印
(0, 3)
(1, 6)
(2, 7)
(3, 12)
(4, 9)https://stackoverflow.com/questions/69313609
复制相似问题