2015-10-22 5 views
2

Я был на этом несколько часов, глядя на каждый веб-сайт и часть документации, которую я мог бы сделать. Я не могу понять, как удалить один и только один элемент (в этом случае, строку) из массива, сохраняя любые дубликаты в такте.Удалите один элемент из массива.

Я сделал найти способ, но это абсолютно зверским:

let remItem gs item = 
    if (chkItem gs item) then 
     let mutable fr = [| |] //temporary array 
     let mutable don = false //check if we found the element 
     for i in gs.inventory do 
      if not (i = item) && don then 
       fr <- (Array.append fr [|i|]) 
      //add to the temp array until we find our item 
      elif i = item && don = false then don <- true 
      //we found it, skip just once so it doesn't get added 
      elif don then fr <- (Array.append fr [|i|]) 
      //now just add everything else to the temp array 
     { gs with inventory = fr } 
    else gs 

Я написал это, и я едва знаю, как это работает. Пожалуйста, скажите, что есть лучший способ сделать это. Я знаю, что изменяемые переменные не нужны, но я написал дюжину одинаково ужасающих чистых функций и пришел к выводу, что это лучшее, что я мог бы сделать. Я уже пробовал много массивов. * Рекурсивные функции уже, я не могу заставить кого-либо из них соответствовать тому, что я хочу. Я просто хочу знать, можно ли делать это аккуратно и чисто в F #.

ответ

5

Я думаю, что самый простой способ сделать это - сначала искать индекс (это массив в конце концов), а затем просто вырезать это - это (я думаю) хороший компромисс между производительностью и чистотой - это чистый операция, но вы не получите гораздо копировальные операции:

let remove x (xs : 'a array) = 
    match Array.tryFindIndex ((=) x) xs with 
    | Some 0 -> xs.[1..] 
    | Some i -> Array.append xs.[..i-1] xs.[i+1..] 
    | None -> xs 

Обратите внимание, что вы должны заботиться о нем неоспоримом первом индекс, потому что xs.[..(-1)] будет сгенерировано исключение (в то время как другой край случай в порядке) :

> remove 0 [|1..10|];; 
val it : int [] = [|1; 2; 3; 4; 5; 6; 7; 8; 9; 10|] 
> remove 1 [|1..10|];; 
val it : int [] = [|2; 3; 4; 5; 6; 7; 8; 9; 10|] 
> remove 3 [|1..10|];; 
val it : int [] = [|1; 2; 4; 5; 6; 7; 8; 9; 10|] 
> remove 9 [|1..10|];; 
val it : int [] = [|1; 2; 3; 4; 5; 6; 7; 8; 10|] 
> remove 10 [|1..10|];; 
val it : int [] = [|1; 2; 3; 4; 5; 6; 7; 8; 9|] 
> remove 11 [|1..10|];; 
val it : int [] = [|1; 2; 3; 4; 5; 6; 7; 8; 9; 10|] 

Если вам нужна еще большая производительность лет u может создать пустой массив и использовать более императивный стиль для копирования деталей:

let remove x (xs : 'a array) = 
    match Array.tryFindIndex ((=) x) xs with 
    | Some i -> 
     let res = Array.zeroCreate (xs.Length-1) 
     if i >= 1 then 
      System.Array.Copy(xs,0,res,0,i) 
     if i+1 < xs.Length then 
      System.Array.Copy(xs,i+1,res,i,xs.Length-i-1) 
     res 
    | None -> xs 
+0

Это сделало это для меня! Благодарю вас. Я пытался индексировать массив, но, эх, продолжал идти за пределы, ха-ха. – Konata

0

Удалить первое вхождение элемента из списка (взято из http://www.fssnip.net/1T):

let rec remove_first pred lst = 
    match lst with 
    | h::t when pred h -> t 
    | h::t -> h::remove_first pred t 
    | _ -> [] 

Использование:

let somelist = [('a',2);('f',7);('a',4);('h',10)] 
let removed = somelist |> remove_first (fun (x,y) -> x='a') 

// Result is: 
// [('f',7);('a',4);('h',10)] 
+0

К сожалению, я не могу использовать решения на основе списка, так как я использую массив. – Konata

+0

Если это проблема с производительностью, List.OfArray и List.ToArray решат это для вас. – Kit

0

Fold должен сделать трюк:

let remove x a = 
    Array.fold 
    (fun (s,found) t -> 
     if found || t <> x then Array.append s [|t|],found 
     else s,true) ([||],false) a |> fst 

Пример использование:

remove 2 [|1; 2; 3; 4; 2; 5|] 
val it : int [] = [|1; 3; 4; 2; 5|] 
Смежные вопросы