F#-如何在循环序列中对上一个、这个和下一个元素进行分组
在F#中,如何最好地将有限序列(如F#-如何在循环序列中对上一个、这个和下一个元素进行分组,f#,F#,在F#中,如何最好地将有限序列(如seq[0;1;2;3;4]转换为元组序列(如seq[4,0,1;0,1,2;1,2,3;2,3,4;3,4,0] 添加: 我的Seq表示循环数据。在这种情况下,闭合多段线的顶点。我需要相邻元素来计算每个角的角度。这里有一个只使用序列的简单解决方案。请注意,如果输入和输出总是一个列表,那么有一个稍微复杂但更快的解决方案,它只使用列表并遍历输入一次 // Example usage: neighbors [0..4] let neighbors input =
seq[0;1;2;3;4]
转换为元组序列(如seq[4,0,1;0,1,2;1,2,3;2,3,4;3,4,0]
添加:
我的Seq表示循环数据。在这种情况下,闭合多段线的顶点。我需要相邻元素来计算每个角的角度。这里有一个只使用序列的简单解决方案。请注意,如果输入和输出总是一个列表,那么有一个稍微复杂但更快的解决方案,它只使用列表并遍历输入一次
// Example usage: neighbors [0..4]
let neighbors input =
let inputLength = Seq.length input
input
// The sequence needs to be looped three times;
// the first and last time handle the previous value for the first
// element in the input sequence and the next value for the last
// element in the input sequence, respectively.
|> Seq.replicate 3
// Start at the previous element of the first element in the input sequence.
|> Seq.skip (inputLength - 1)
// Take the same number of elements as the tuple.
|> Seq.windowed 3
// Only keep the same number of elements as the original sequence.
|> Seq.take inputLength
// Convert the arrays to tuples
|> Seq.map (fun values ->
values.[0], values.[1], values.[2])
// Return the result as a list of tuples.
|> Seq.toList
这给出了正确的答案,虽然你拥有的元素现在是最后一个,但这不是问题,你仍然可以找到每一组三点的角度
let cycle s =
Seq.append s (Seq.take 2 s) // append the first two elements to the and
|> Seq.windowed 3 // create windows of 3
|> Seq.map (fun a -> (a.[0], a.[1], a.[2])) // create tuples
// test
[0;1;2;3;4] |> cycle
// returns:
>
val it : seq<int * int * int> =
seq [(0, 1, 2); (1, 2, 3); (2, 3, 4); (3, 4, 0); ...]
let循环s=
Seq.append s(Seq.take 2 s)//将前两个元素附加到and
|>Seq.windowed 3//创建3个窗口
|>Seq.map(乐趣a->(a[0],a[1],a[2])//创建元组
//试验
[0;1;2;3;4]|>循环
//返回:
>
val it:seq=
seq[(0,1,2);(1,2,3);(2,3,4);(3,4,0);…]
让windowedEx n(序列号)=
设r=调整数组大小
如果r.计数>1,则
让last=r[r.Count-1]
r、 添加(r[0])
r、 插入(0,最后一个)
顺序加窗n r
这里有一些很好的答案,这里还有另一个。对我来说,它看起来最具可读性,具有O(n)
的复杂性,并且还保留了一些错误检查:
// Returns the last element of a sequence.
// Fails on empty sequence
let last xs =
let len = Seq.length xs - 1
if len < 0 then failwith "Sequence must be non-empty"
xs
|> Seq.skip len
|> Seq.head
// Converts an array into a tuple
let toTuple = function
| [|a; b; c|] -> (a, b, c)
| _ -> failwith "Must be an array with exactly 3 elements"
let windowedBy3 xs =
seq {
yield last xs;
yield! xs;
yield Seq.head xs
}
|> Seq.windowed 3
|> Seq.map toTuple
// Usage
Seq.init 5 id
|> windowedBy3
|> Seq.iter (printf "%A; ")
//返回序列的最后一个元素。
//在空序列上失败
让最后一个X=
设len=Seq.length xs-1
如果len<0,则failwith“序列必须为非空”
xs
|>序号:跳过透镜
|>序号头
//将数组转换为元组
让toTuple=函数
|[a;b;c]->(a,b,c)
|->failwith“必须是正好包含3个元素的数组”
让windowedby3xs=
序号{
产生最后的X;
屈服!xs;
收益率序列头xs
}
|>序号3加窗
|>Seq.map toTuple
//用法
Seq.init 5 id
|>加窗3
|>序号iter(打印文件“%A;”)
如果您不需要懒散,使用中间数组可能更有效,例如
// get items (i-1, i, i+1) from arr; wrap around at the boundaries
let adj3 i (arr: 'a[]) =
// modulo operator that works correctly
let inline (%%) x y = ((x % y) + y) % y
let len = arr.Length
arr.[(i - 1) %% len], arr.[i], arr.[(i + 1) %% len]
let windowed3 s = seq {
let sarr = s |> Seq.toArray
for i = 0 to sarr.Length do
yield adj3 i sarr }
时间复杂性以O(n)为单位。我会这样做:
let neighbors xs =
match Array.ofSeq xs with
| [||] -> [||]
| [|x|] -> [|x, x, x|]
| xs ->
let n = xs.Length
[|yield xs.[n-1], xs.[0], xs.[1]
for i=1 to n-2 do
yield xs.[i-1], xs.[i], xs.[i+1]
yield xs.[n-2], xs.[n-1], xs.[0]|]
比较通常比模整数运算快得多。为了加快速度,请预先分配数组并填充元素,而不是使用序列表达式。对于
Seq.circularIndowed
的一般解决方案是什么?给定窗口大小n
,它需要先消耗第一个n-1
元素,同时保留其余元素的懒散性。如果源中的元素不超过n-1
,它将生成一个空序列
所以它是一个用于缓存的调整大小数组和一个用于将其缝合在一起的序列表达式
module Seq =
let circularWindowed n (xs : seq<_>) =
let en = xs.GetEnumerator()
let ra = ResizeArray()
while ra.Count < n - 1 && en.MoveNext() do
ra.Add en.Current
seq{
if en.MoveNext() then
yield! ra
yield en.Current
while en.MoveNext() do
yield en.Current
yield! ra }
|> Seq.windowed n
seq [0; 1; 2; 3; 4]
|> Seq.circularWindowed 3
|> Seq.toList
// val it : int [] list =
// [[|0; 1; 2|]; [|1; 2; 3|]; [|2; 3; 4|]; [|3; 4; 0|]; [|4; 0; 1|]]
模块序列=
让循环输入n(xs:seq)=
设en=xs.GetEnumerator()
设ra=ResizeArray()
而ra.Count带窗口的
序号[0;1;2;3;4]
|>序号3
|>序号:toList
//val it:int[]列表=
// [[|0; 1; 2|]; [|1; 2; 3|]; [|2; 3; 4|]; [|3; 4; 0|]; [|4; 0; 1|]]
@bytebuster是的,没错;但考虑到样本输入,这可能无关紧要。如果可能有重复的值,那么最好实现我提到的基于列表的解决方案。@bytebuster今天早上仔细考虑后,我想到了一个更好的方法来处理这个问题,它不需要Seq.distinct,也不会遇到任何重复值的问题。如果首先调用Seq.length,使用Seq有多大意义?Seq.replicate也是一个扩展吗?@jk我在这里使用Seq
的唯一原因是保持这个函数的通用性(因为它可以处理序列、列表、数组等)。正如我在回答中提到的,如果您知道输入总是一种更具体的集合类型,如列表
或数组
,则有更快的方法实现此功能Seq.replicate
是中提供的函数。我喜欢你的模运算符。如果我需要从一开始就知道seq的最后一个元素,那么seq还会懒惰吗?不,我不这么认为。获取序列的最后一个元素需要至少枚举/强制整个序列一次(这是Seq.toArray
所做的,也是Seq.skip len
,ResizeArray
等所做的)。
module Seq =
let circularWindowed n (xs : seq<_>) =
let en = xs.GetEnumerator()
let ra = ResizeArray()
while ra.Count < n - 1 && en.MoveNext() do
ra.Add en.Current
seq{
if en.MoveNext() then
yield! ra
yield en.Current
while en.MoveNext() do
yield en.Current
yield! ra }
|> Seq.windowed n
seq [0; 1; 2; 3; 4]
|> Seq.circularWindowed 3
|> Seq.toList
// val it : int [] list =
// [[|0; 1; 2|]; [|1; 2; 3|]; [|2; 3; 4|]; [|3; 4; 0|]; [|4; 0; 1|]]