在不使用嵌套地图的情况下为列表列表实现地图功能

时间:2018-10-26 19:36:35

标签: functional-programming f# sml

我给自己设定了以下挑战(失败了):

我想编写一个映射函数map f lofls,它需要一个函数f 'a -> 'b和一个列表列表lofls 'a list list并在其中应用函数f列表列表中的每个元素。我添加的约束是不允许将嵌套映射用于列表,而必须递归地进行。

我试图用F#来做,但是任何语言都应该做。有什么想法吗?

编辑

这是我的尝试(虽然有效,但很丑陋,我也不喜欢使用rev。)。

let map f lis = 
    let rec map2 f lis aux =
        match (lis, aux) with
        |([], []) -> []
        |([], aux) -> [aux]
        |(hd::tl, aux) ->
            match hd with 
            |[] -> (List.rev aux) :: (map2 f tl [])
            |x::xs -> map2 f (xs::tl) ( (f x) :: aux )
    map2 f lis []

(我也意识到这已经以更简洁的形式发布了)

5 个答案:

答案 0 :(得分:5)

从简单到复杂,逐步进行。

这是您希望map函数具有的签名:

('a -> 'b) -> 'a list list -> 'b list list

简单的解决方案是这样:

let map0 (f:'a -> 'b) (lofls:'a list list) : 'b list list = lofls |> List.map (List.map f)

但是那不是递归的,它使用嵌套映射。

递归解决方案可能是这样:

let rec map1 (f:'a -> 'b) (lofls:'a list list) : 'b list list =
    match lofls with
    | []      -> []
    | l::rest -> (List.map f l) :: map1 f rest

尽管它仍在其中调用List.map,但它是递归的。

所以,这是下一个级别:

let rec map (f:'a -> 'b) (lofls:'a list list) : 'b list list =
    match  lofls                    with
    | [          ]         -> [              ]
    | [          ] :: rest -> [              ] :: (rest |> map f)
    | ( e::restl ) :: rest -> 
    match  restl   :: rest |> map f with
    | [          ]         -> [              ]
    | [          ] :: rest -> [ f e          ] ::  rest
    | (    restl ) :: rest -> ( f e :: restl ) ::  rest

答案 1 :(得分:3)

另一种方式:

let rec mapNested f lofls =
    match lofls with
    | []   -> []
    | h::t -> (map f h) :: (mapNested f t)
and map f lst = 
    match lst with
    | []   -> []
    | h::t -> (f h) :: (map f t)

答案 2 :(得分:2)

如果这是一个家庭作业问题(我确定不是),答案取决于构成“嵌套列表列表”的原因。

map [] (map [] f)这样的结构可以用流水线重写为f |> map [] |> map []或将函数组合运算符替换为(map [] >> map []) f,但仍可以视为嵌套映射。

let mapNested f =
    let rec map acc g = function
    | [] -> List.rev acc
    | x::xs -> map (g x::acc) g xs
    f |> map [] |> map [] 
// val mapNested : f:('a -> 'b) -> ('a list list -> 'b list list)

这是展示您对lambda演算和Y combinator的掌握的机会。 map函数作为参数的嵌套传递应该明显传递参数。

let rec Y f x = f (Y f) x

let map f acc g = function
| [] -> List.rev acc
| x::xs -> f (g x::acc) g xs

let map1 f =
    Y map [] f
// val map1 : f:('a -> 'b) -> ('a list -> 'b list)

let map2 f =
    Y map [] f
    |> Y map []
// val map2 : f:('a -> 'b) -> ('a list list -> 'b list list)

答案 3 :(得分:0)

尾部递归方式

let mapNested f lofls =
    let rec map f lst acc =
        match lst with
        | []   -> List.rev acc
        | h::t -> map f t (f h :: acc)
    map (fun x -> map f x []) lofls []

答案 4 :(得分:0)

我不确定为什么用SML标记这个问题,但是既然如此,这就是在SML中如何完成的事情:

首先,这是您明确避免的惯用解决方案:

remove

(如果不是ML的value restriction,您可以写fun mapmap f = map (map f) 。)

如果您想使用显式递归编写val mapmap = map o map

mapmap

使用单个显式递归函数很难编写此函数的原因之一是调用堆栈用于两件事:

  1. 收集每个内部列表的结果,
  2. 收集外部列表的结果。

可以将调用堆栈的这些用法之一转换为带有累积参数的显式堆栈。这就是例如定义了尾递归fun mapmap f [] = [] | mapmap f (xs::xss) = map f xs :: mapmap f xss and map f [] = [] | map f (x::xs) = f x :: map f xs

rev

fun rev xs = let fun aux [] acc = acc | aux (x::xs) acc = aux xs (x::acc) in aux xs [] end 的接口类似,不需要累加参数,因此可以将其隐藏在内部帮助器函数中。因此,通过这种显式记帐,可以对内部列表和外部列表执行显式递归的单个函数变得很复杂:

mapmap