List 替换列表元素是反模式吗?
我有一个模块,它在以列表表示的路径上工作。大多数函数都执行典型的递归列表处理,但现在我需要一个有时会改变路径的函数。因此,我编写了这个List 替换列表元素是反模式吗?,list,f#,anti-patterns,List,F#,Anti Patterns,我有一个模块,它在以列表表示的路径上工作。大多数函数都执行典型的递归列表处理,但现在我需要一个有时会改变路径的函数。因此,我编写了这个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
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]
通常,当我觉得有必要扩充一个内置模块时,我最终会发现我在做一些古怪或低效的事情。替换列表元素是其中之一吗?有更简单(同样有效)的方法吗?您可以使用
列表编写。折叠:
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
它稍微简单一些,并且具有类似的性能(列表元素上的单个循环)
更新
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
*)
让我们替换pf sf xs=
让find=ref为false
设rec aux=函数
| [] -> []
|x::xs->if pf x则find:=true;(sf x)::xs其他x::(辅助xs)
设xs=aux-xs
如果!然后查找一些xs,其他则没有
(*
>设xs=[0..9];;
val xs:int list=[0;1;2;3;4;5;6;7;8;9]
>让subf=fun->-1;;
val subf:'a->int
>替换((=)5)子项xs;;
val it:int list选项=一些[0;1;2;3;4;-1;6;7;8;9]
>替换((替换(=)50)子项xs;;
val it:int list选项=无
*)
如果您的应用程序可以接受复杂性,那么您的代码就是完美的。为了获得更好的复杂性,您需要解决线性搜索的需要,例如对元素施加顺序和使用二进制搜索树
不涉及搜索的一个相关问题是用已知索引替换列表元素:
val replaceAt : int -> 'a -> 'a list -> 'a list
对于这个问题,存在比标准列表更好的持久数据结构。在文献中搜索纯函数随机访问列表
奇怪的是,没有ML家族语言(OCaml、F#、SML)在标准列表库中定义replace
或replaceAt
。这可能是为了鼓励用户重新设计代码以避免O(N)
这些操作的复杂性。您不是在改变列表,而是在构建替换的结果。O(N)
复杂性是否适合您的应用程序?如果是,您的代码是完美的;如果不是,您有两个选择:使用可变的DS,例如带有O(1)
update但O(N)的数组
clone;或者在文献中搜索纯功能随机访问列表(如冈崎)。@toyvo:我认为很明显我没有对列表进行变异(从技术意义上讲),因为这是不可能的。重建是唯一的选择。问题是,这个操作是否表明使用了错误的DS?搜索本身是O(N)(我不需要改进这一点)因此使用数组不会改变渐进复杂性(尽管它会大大提高实际成本)。@Daniel很抱歉用了这个词。是的,我没有密切注意,如果你在进行线性搜索,那么数组和RA列表都不会有任何帮助。那么列表就是正确的(简单)你能为你的元素排序吗?这将放入log(N)
在表上搜索,比如说,在地图上搜索日志更新。也许我太担心了。缺少内置函数让我觉得我可能错过了一些明显的东西。@Daniel,奇怪的是SML和OCaml标准库都没有它:而且-我想这只是因为它是O(N)
.OCaml支持assoc列表-也支持线性搜索。但它没有替换,我想通过assoc列表,您可以通过(:)实现相同的效果
。是的,我所做的本质上是一个映射,但我避开了列表。映射
,因为它需要可变性来跟踪列表是否发生了变化。虽然要短得多。+1此代码做了一些不同的事情:它替换与谓词pf
匹配的所有元素,而问题中的代码只替换第一次出现的元素(如果!c=0&&(pfx).
很容易修复)@丹尼尔-嗯,我认为基本上是一样的。@MiMo-谢谢你指出。我误解了丹尼尔替换的行为。但是,正如已经显示的那样,更改似乎很容易。@BLUEPIXY:就我而言,差异并不重要,因为始终会有0或1个匹配元素。这里是List.rev
,对吗这是一个额外的遍历吗?我想你是对的-这是一个额外的遍历-但自从我使用F#以来已经有一段时间了。。。
val replaceAt : int -> 'a -> 'a list -> 'a list