Clojure有一个很好的函数叫做分区,它可以处理序列。它将给定的序列分成一系列同样长的列表。第一个参数指定fragaments的长度。第二个参数是一个偏移量,它指定片段的下一个开始。
(partition 3 1 (range 5))
;;=> ((0 1 2) (1 2 3) (2 3 4))
(partition 4 6 (range 20))
;;=> ((0 1 2 3) (6 7 8 9) (12 13 14 15))
(partition 4 3 (range 20))
;;=> ((0 1 2 3) (3 4 5 6) (6 7 8 9) (9 10 11 12) (12 13 14 15) (15 16 17 18))
https://clojuredocs.org/clojure.core/partition
我正在寻找F#中的等效函数。显然List.partition做了别的事情(https://msdn.microsoft.com/en-us/library/ee353782.aspx)。也许有一个图书馆提供这样的功能?
答案 0 :(得分:7)
在F#中,你有两个相似的函数:windowed
,它类似于Clojure的分区,但第二个参数固定为1,chunkBySize
,第二个参数等于第一个参数。
您可以将两者结合起来并获得所需的功能。以下是列表示例:
let partition x y = List.windowed x >> List.chunkBySize y >> List.map List.head
它们也可用于数组和序列,但请注意,对于序列,内部集合将是一个数组,实际上是一个序列。因此,如果您希望将结果严格推断为序列序列,则必须添加转换或向上转换:
let partition x y = Seq.windowed x >> Seq.chunkBySize y >> Seq.map (Seq.head >> seq)
答案 1 :(得分:2)
你可以自己实现这个功能,正如@Gustavo所证明的那样,这并不难。一个问题是你刚刚完成的功能是否正常工作。
所以这是一个使用wait来测试分区函数hold的属性的实现和属性测试。
FsCheck
是一个很棒的工具,可以检查您刚刚实现的功能是否具有正确的属性。
open FsCheck
let partition (size : int) (increment : int) (vs : 'T []) : 'T [] [] =
let size = max 1 size
let increment = max 1 increment
let length = vs.Length
[| for i in [size..increment..length] -> vs.[(i - size)..(i - 1)] |]
let range (n : int) : int [] = [| 0..(n - 1) |]
type Properties() =
static member ``all partitions have the right size`` (size : int, increment : int, vs : int []) =
(size > 1 && increment > 1 && vs.Length > 1) ==>
fun () ->
let partitions = partition size increment vs
// Iterates over partitions and make sure they have the same Length = size
let rec check = function
| i when i <
partitions.Length -> partitions.[i].Length = size && (check (i + 1))
| _ -> true
check 0
static member ``all partitions have been incremented the right way`` (size : int, increment : int, vs : int []) =
(size > 1 && increment > 1 && vs.Length > 1) ==>
fun () ->
let ivs = vs |> Array.mapi (fun i v -> (i,v))
let partitions = partition size increment ivs
// Iterates over partitions and make sure the first element has the right increment
let rec check = function
| i when i < partitions.Length ->
let ii, vv = partitions.[i].[0]
ii = (i*increment) && vv = vs.[ii] && (check (i + 1))
| _ -> true
check 0
static member ``all partitions have the right content`` (size : int, increment : int, vs : int []) =
(size > 1 && increment > 1 && vs.Length > 1) ==>
fun () ->
let ivs = vs |> Array.mapi (fun i v -> (i,v))
let partitions = partition size increment ivs
// Iterates over partitions and make sure the each in the partition is correct element
let rec check = function
| i when i < partitions.Length ->
let partition = partitions.[i]
let exp = i*increment
let rec check_partition = function
| i when i < partition.Length ->
let ii, vv = partition.[i]
ii = (exp + i) && vv = vs.[ii] && check_partition (i + 1)
| _ -> true
check_partition 0 && (check (i + 1))
| _ -> true
check 0
[<EntryPoint>]
let main argv =
range 5 |> partition 3 1 |> printfn "%A"
range 20 |> partition 4 6 |> printfn "%A"
range 20 |> partition 4 3 |> printfn "%A"
let config = { Config.Quick with MaxFail = 100000; MaxTest = 1000 }
// Generates random data and make sure the properties holds for any random data
Check.All<Properties> config
0
所以即使你没有要求这个,我想也许你对它感兴趣。