删除单个非唯一值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