【问题标题】:Implement a map functional for a list of lists without using nested maps在不使用嵌套映射的情况下为列表列表实现映射功能
【发布时间】:2019-03-31 14:26:42
【问题描述】:

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

我想编写一个映射函数 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 []

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

【问题讨论】:

  • 你有什么代码可以显示你失败的尝试吗?你具体在哪里遇到了困难?
  • 抱歉回复晚了,这里是上面的尝试。

标签: functional-programming f# sml


【解决方案1】:

让我们一步一步来,从简单到复杂。

这是您希望 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

【讨论】:

  • 非常感谢!这正是我一直在寻找的答案!
【解决方案2】:

另一种方式:

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)

【讨论】:

  • 好答案。虽然map 和mapNested 不需要与and 连接。只需将map 放在首位。
  • mapNested f = map (map f)
【解决方案3】:

尾递归方式

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 []

【讨论】:

  • 虽然它不会为大型列表生成StackOverflowException,但它仍然是map 的嵌套调用,不是吗?
【解决方案4】:

如果这是一个家庭作业问题,我确信它不是,答案取决于什么构成“列表的嵌套映射”。

像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)

【讨论】:

    【解决方案5】:

    我不确定为什么这个问题会被标记为 SML,但既然是这样,那么在 SML 中可以这样做:

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

    fun mapmap f = map (map f)
    

    (如果不是 ML 的 value restriction,你可以写 val mapmap = map o map。)

    如果您想使用显式递归编写mapmap:

    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
    

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

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

    调用堆栈的其中一种用途可以在累加参数中转换为显式堆栈。这就是例如定义了一个尾递归rev:

    fun rev xs =
      let fun aux [] acc = acc
            | aux (x::xs) acc = aux xs (x::acc)
      in aux xs [] end
    

    mapmap 的接口中同样不需要累加参数,因此可以将其隐藏在内部辅助函数中。因此,在内部和外部列表上执行显式递归的单个函数会因这种显式簿记而变得复杂:

    fun mapmap f xss =
      let fun aux f [] _ = []
            | aux f ([]::xss) ys = rev ys :: aux f xss []
            | aux f ((x::xs)::xss) ys = aux f (xs::xss) (f x :: ys)
      in aux f xss [] end
    

    【讨论】:

      猜你喜欢
      • 2019-11-28
      • 1970-01-01
      • 1970-01-01
      • 2017-05-25
      • 2020-04-02
      • 2018-04-21
      • 2020-04-06
      • 2014-04-09
      • 1970-01-01
      相关资源
      最近更新 更多