2016-02-14 46 views
1

我想采用List或Array,并在集合中给出两个元素,获取它们之间的所有元素。但我想以循环的方式做到这一点,例如[1;2;3;4;5;6],如果我要求介于4和2之间的元素,我可以回到[5;6;1]获取F#集合中两个元素之间的元素

正在习惯于命令式编程我可以轻松地做到这一点循环,但我想在F#中可能有更好的惯用方法。

编辑

这里是我想出了一个办法,在找到一个Array.indexed功能

let elementsBetween (first:int) (second:int) (elements: array<'T>) = 
    let diff = second - first 
    elements 
    |> Array.indexed 
    |> Array.filter (fun (index,element) -> if diff = 0 then false 
              else if diff > 0 then index > first && index < second 
              else if diff < 0 then index > first || index < second 
              else false 

这种做法只会使用数组显然但这似乎还不错。我有一种感觉,我可以通过用模式匹配替换if/then/else来清理它,但不知道如何干净地做到这一点。

+2

您需要提供您的尝试。虽然我们大多数人都可以轻松回答这个问题,但您需要展示自己的努力。因此,我正在给这个投票。 –

+3

重复的情况下会发生什么?如果没有找到其中一个极限值会怎么样? –

+0

谢谢你展示你的尝试。我扭转了我的投票。 –

回答

0

下面是使用与列表和列表差异的想法变化切片 <some list.[x .. y]

let between (first : int) (second : int) (l : 'a list) : 'a list = 
    if first < 0 then 
     failwith "first cannot be less than zero" 
    if second < 0 then 
     failwith "second cannot be less than zero" 
    if first > (l.Length * 2) then 
     failwith "first cannot be greater than length of list times 2" 
    if second > (l.Length * 2) then 
     failwith "second cannot be greater than length of list times 2" 

    let diff = second - first 
    match diff with 
    | 0 -> [] 
    | _ when diff > 0 && (abs diff) < l.Length -> l.[(first + 1) .. (second - 1)] 
    | _ when diff > 0 -> ([email protected]).[(first + 1) .. (second - 1)] 
    | _ when diff < 0 && (abs diff) < l.Length -> l.[(second + 1) .. (second + first - 1)] 
    | _ when diff < 0 -> ([email protected]).[(second + 1) .. (second + first - 1)] 
+0

这看起来像只有'first'既是索引又是第一个元素的值时才会起作用。也许我误解了,但我认为“[1; 2; 3; 4; 5; 6]”只是一个例子。我认为这不会适用于像[11; 13; 14; 16]这样的任意缓冲区。这需要列表以0开始[0; 1; 2; 3; 4; 5; 6]' –

+0

@PhillipScottGivens我没有检查代码的确切正确性,因为OP在评论中声明'我不需要准确的代码只是一些一般的方法。“由于答案是CC,因此可以随意编辑答案。 –

2

我不在我的普通计算机上使用f#编译器,所以我还没有测试过它。它应该看起来像这样

[编辑]谢谢@FoggyFinder给我看https://dotnetfiddle.net/。我现在用它测试了下面的代码。

[编辑]这应该在一次通过中找到圆形范围。

let x = [1;2;3;4;5] 
let findCircRange l first second = 
    let rec findUpTo (l':int list) f (s:int) : (int list * int list) = 
     match l' with 
     | i::tail -> 
      if i = s then tail, (f []) 
      else findUpTo tail (fun acc -> f (i::acc)) s 
     // In case we are passed an empty list. 
     | _ -> [], (f []) 
    let remainder, upToStart = findUpTo l id first 
    // concatenate the list after start with the list before start. 
    let newBuffer = [email protected] 
    snd <| findUpTo newBuffer id second 

let values = findCircRange x 4 2 
printf "%A" values 

findUpTo接受一个列表(l'),一个用于创建余数列表(f)的函数和一个用于查找(s)的值。我们通过它递归(尾递归)来查找给定值以及给定值之后的列表。将余下的末尾附加到周围的缓冲区中。再次将它传递给findUpTo以查找到最后。将缓冲区返回到最后。

我们通过一个函数来累计找到的项目。这种技术允许我们在函数调用展开时追加到列表的末尾。

当然,这里没有错误检查。我们假设开始和结束确实存在。这将留给读者一个练习。

+1

谢谢,很高兴看到列表和数组的方法。 – jackmott

+1

你可以在这里测试简单的代码,例如:https://dotnetfiddle.net/ –

+0

@FoggyFinder谢谢。我通常使用www.tryfsharp.com,但它需要我没有在该计算机上安装。 –

2

您应该看看MSDN Collections.Seq模块。

让我们尝试要巧:

let elementsBetween a e1 e2 = 
    let aa = a |> Seq.append a 
    let i1 = aa |> Seq.findIndex (fun e -> e = e1) 
    let i2 = aa |> Seq.skip i1 |> Seq.findIndex (fun e -> e = e2) 
    aa |> Seq.skip(i1+1) |> Seq.take(i2-1)