我有一个模块适用于表示为列表的路径。大多数函数都执行典型的递归列表处理,但现在我需要一个有时会改变路径的函数。所以,我写了这个replace
函数:
module List =
let replace f sub xs =
let rec finish acc = function
| [] -> acc
| x::xs -> finish (x::acc) xs
let rec search acc = function
| [] -> None
| x::xs ->
if f x then Some(finish ((sub x)::xs) acc)
else search (x::acc) xs
search [] xs
的工作原理如下:
let xs = List.init 10 id
let res = List.replace ((=) 5) (fun _ -> -1) xs
//Some [0; 1; 2; 3; 4; -1; 6; 7; 8; 9]
通常,当我觉得需要增加一个内置模块时,我最终发现我正在做一些古怪或低效的事情。将list元素替换为其中之一?有没有更简单(同样有效)的方法来做到这一点?
答案 0 :(得分:5)
如果您的应用程序可以接受O(N)
复杂性,那么您的代码就是完美的。为了更好的复杂性,您可能希望解决线性搜索的需要,例如通过对元素强加顺序和使用二叉搜索树。
不涉及搜索的相关问题是用已知索引替换列表元素:
val replaceAt : int -> 'a -> 'a list -> 'a list
对于此问题,存在比标准列表更好的持久性数据结构。在文献中搜索纯功能随机访问列表。
奇怪的是,没有ML系列语言(OCaml,F#,SML)在标准列表库中定义replace
或replaceAt
。这可能是为了鼓励用户重新设计他们的代码,以避免这些操作的O(N)
复杂性。
答案 1 :(得分:4)
您可以使用List.fold
:
let replace f sub xs =
let processItem (found,list) x =
if found then (true,x::list)
elif f x then (true,(sub x)::list)
else (false,x::list)
let (found, list) = xs |> List.fold processItem (false,[])
if found then Some(List.rev list)
else None
它稍微简单,性能相似(在列表元素上有一个循环)。
答案 2 :(得分:2)
let replace pf el xs =
let c = ref 0
let xs = List.map (fun x -> if pf x then incr c;el else x) xs
if !c = 0 then None else Some xs
(*
> replace ((=)5) -1 [0..9];;
val it : int list option = Some [0; 1; 2; 3; 4; -1; 6; 7; 8; 9]
> replace ((=)10) -1 [0..9];;
val it : int list option = None
*)
<强>更新强>
let replace pf sf xs =
let find = ref false
let rec aux = function
| [] -> []
| x::xs -> if pf x then find := true;(sf x) :: xs else x :: (aux xs)
let xs = aux xs
if !find then Some xs else None
(*
> let xs = [0..9];;
val xs : int list = [0; 1; 2; 3; 4; 5; 6; 7; 8; 9]
> let subf = fun _ -> -1;;
val subf : 'a -> int
> replace ((=) 5) subf xs;;
val it : int list option = Some [0; 1; 2; 3; 4; -1; 6; 7; 8; 9]
> replace ((<) 5) subf xs;;
val it : int list option = Some [0; 1; 2; 3; 4; 5; -1; 7; 8; 9]
> replace ((=) 50) subf xs;;
val it : int list option = None
*)