F#是一种“单次”编译器,因此您可以期望任何编译错误都会在错误下方产生级联影响。当您有编译错误时,请专注于该单个错误。虽然您的代码可能存在更多错误(确实有),但后续错误也可能只是第一个错误的结果。
就像编译器所说,car
不可变,因此不能给它赋值。
在函数式编程中,映射可以很容易地实现为递归函数:
// ('a -> 'b) -> 'a list -> 'b list
let rec map f = function
| [] -> []
| h::t -> f h :: map f t
然而,这个版本不是尾递归的,因为它在将头部连接到尾部之前递归调用了map
。
通常可以通过引入一个使用累加器作为结果的“内部”实现函数来重构为尾递归实现。以下是一种方法:
// ('a -> 'b) -> 'a list -> 'b list
let map' f xs =
let rec mapImp f acc = function
| [] -> acc
| h::t -> mapImp f (acc @ [f h]) t
mapImp f [] xs
在这里,mapImp
是在 h::t
情况下被调用的最后一个操作。
这个实现有点低效,因为它在每次迭代中都要连接两个列表(acc @ [f h]
)。根据要映射的列表的大小,通过在末尾进行单个反转来将累加器 cons 起来可能更有效:
// ('a -> 'b) -> 'a list -> 'b list
let map'' f xs =
let rec mapImp f acc = function
| [] -> acc
| h::t -> mapImp f (f h :: acc) t
mapImp f [] xs |> List.rev
无论如何,做所有这些的唯一原因是为了练习,因为
此函数已经内置。
在所有情况下,您都可以使用映射函数将列表中的所有元素乘以二:
> let mdouble = List.map (
通常情况下,我甚至不用明确定义这样的函数。相反,您可以在内联中使用它:
> List.map (
你可以以相同的方式使用上述所有地图函数。
ax
参数。function
语句隐式地接受参数。否则,您可以阅读整个match ax with
语句并保留ax
参数不变。 - Bartek Kobyłecki