刪除單個非唯一值I具有表示在F#骰子整數的序列。從序列中F#
在有問題的遊戲中,玩家擁有骰子池,可以選擇打一個(按照一定的規則管轄),並保持休息。
例如,如果玩家擲出6,6和4並決定玩六分之一,是否有一種簡單的方法可以返回一個只有一個6的序列?
Seq.filter (fun x -> x != 6) dice
刪除所有六個,不只是一個。
刪除單個非唯一值I具有表示在F#骰子整數的序列。從序列中F#
在有問題的遊戲中,玩家擁有骰子池,可以選擇打一個(按照一定的規則管轄),並保持休息。
例如,如果玩家擲出6,6和4並決定玩六分之一,是否有一種簡單的方法可以返回一個只有一個6的序列?
Seq.filter (fun x -> x != 6) dice
刪除所有六個,不只是一個。
下面的代碼將工作的列表(因此不會受到任何序列,但它聽起來像你的使用可能是一個列表的順序)
let rec removeOne value list =
match list with
| head::tail when head = value -> tail
| head::tail -> head::(removeOne value tail)
| _ -> [] //you might wanna fail here since it didn't find value in
//the list
編輯:基於下面的正確註釋代碼更新。由於P
編輯:讀了不同的答案後,我認爲一個警告是爲了。不要將上面的代碼用於infite序列,但是因爲我猜你的玩家沒有infite dice,這不應該是一個問題,但是爲了完整性,這裏的一個實現可以用於(幾乎)任何 有限序列
let rec removeOne value seq acc =
match seq.Any() with
| true when s.First() = value -> seq.Skip(1)
| true -> seq.First()::(removeOne value seq.Skip(1))
| _ -> List.rev acc //you might wanna fail here since it didn't find value in
//the list
但是,我建議使用第一種解決方案,即使您必須首先將序列轉換爲列表(至少對於小序列或最後尋找值的大序列),我相信後者的表現會比後者更好。
我不覺得有什麼,它會讓你直接表示要刪除剛剛的第一個元素匹配指定CR想法的任何功能列表中的(例如)像Seq.removeOne
)。
您可以實現以相對可讀的方式使用Seq.fold
(如果數序列是有限的)的函數:
let removeOne f l =
Seq.fold (fun (removed, res) v ->
if removed then true, v::res
elif f v then true, res
else false, v::res) (false, []) l
|> snd |> List.rev
> removeOne (fun x -> x = 6) [ 1; 2; 6; 6; 1 ];
val it : int list = [1; 2; 6; 1]
的fold
功能保持一些狀態 - 在本例bool * list<'a>
類型。布爾標誌表示我們是否已經移除了某個元素,並且該列表用於累加結果(在處理結束時必須反轉)。
如果您需要爲(可能)無限seq<int>
這麼做,那麼您需要直接使用GetEnumerator
並將該代碼實現爲遞歸序列表達式。這是一個有點難看,它應該是這樣的:
let removeOne f (s:seq<_>) =
// Get enumerator of the input sequence
let en = s.GetEnumerator()
let rec loop() = seq {
// Move to the next element
if en.MoveNext() then
// Is this the element to skip?
if f en.Current then
// Yes - return all remaining elements without filtering
while en.MoveNext() do
yield en.Current
else
// No - return this element and continue looping
yield en.Current
yield! loop() }
loop()
序列上不平凡的行動是痛苦的工作,因爲他們不支持模式匹配。我認爲,最簡單的解決辦法如下:
let filterFirst f s =
seq {
let filtered = ref false
for a in s do
if filtered.Value = false && f a then
filtered := true
else yield a
}
只要可變實現從客戶端隱藏,它仍然是實用的風格;)
如果你要存儲數據,我會用ResizeArray而不是序列。它具有豐富的功能,如您詢問的功能。它簡稱爲Remove。注意:ResizeArray是CLI類型List的縮寫。
let test = seq [1; 2; 6; 6; 1; 0]
let a = new ResizeArray<int>(test)
a.Remove 6 |> ignore
Seq.toList a |> printf "%A"
// output
> [1; 2; 6; 1; 0]
其他數據類型的選擇可能是陣列
let removeOneFromArray v a =
let i = Array.findIndex ((=)v) a
Array.append a.[..(i-1)] a.[(i+1)..]
或列表
let removeOneFromList v l =
let rec remove acc = function
| x::xs when x = v -> List.rev acc @ xs
| x::xs -> remove (x::acc) xs
| [] -> acc
remove [] l
我不明白「以避免串聯......」的一部分,特別是因爲有一個'@'在你的函數中。是否((List.rev a)@ b'被F#編譯成revappend?即便如此,這仍然是一個串聯,你可以通過簡單地將遍歷的值存儲在堆棧中來避免:'| heat :: tail當head <> value - > head::(removeOne value tail)時'' – 2010-04-24 20:56:51
另外,對'seq.Skip(1)'使用嵌套調用會導致非常低效的代碼(事實上,_O(n) _訪問時間,因爲每次調用'Skip'都會創建一個間接訪問)。使用F#列表時使用的模式根本不適用於序列。 – 2010-04-24 21:33:36
@Tomas是的,當我指出在手邊創建列表時,我的觀點可能會很好地表現得更好 – 2010-04-25 00:47:29