Functional programming 在不使用嵌套映射的情况下为列表列表实现映射函数

Functional programming 在不使用嵌套映射的情况下为列表列表实现映射函数,functional-programming,f#,sml,Functional Programming,F#,Sml,我给自己设置了以下挑战(但失败了): 我想写一个map函数,map f lofls,它接受一个函数f'a->'b和一个列表,lofls'a list list,并在列表的每个元素上应用函数f。我添加的约束是,不允许我对列表使用嵌套映射,我必须递归地这样做 我试着用F#来做,但任何语言都可以。有什么想法吗 编辑 这是我的尝试(虽然有效,但很难看,我也不喜欢使用rev…) (我也意识到这已经以更简洁的形式发布了)让我们一步一步走,从简单到复杂 这是您希望map函数具有的签名: ('a->'b)->'

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

我想写一个map函数,
map f lofls
,它接受一个函数f
'a->'b
和一个列表,lofls
'a list list
,并在列表的每个元素上应用函数
f
。我添加的约束是,不允许我对列表使用嵌套映射,我必须递归地这样做

我试着用F#来做,但任何语言都可以。有什么想法吗

编辑 这是我的尝试(虽然有效,但很难看,我也不喜欢使用rev…)


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

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

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

('a->'b)->'a列表->'b列表

简单的解决方案是:

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
另一种方式:

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)
尾部递归方法

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

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

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微积分和。
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)

我不知道为什么这个问题会被标记为SML,但既然是这样,下面是如何在SML中完成的:

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

fun mapmap f = map (map f)
(如果不是针对ML,您可以编写
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
使用单个显式递归函数很难编写此函数的一个原因是调用堆栈用于两件事:

  • 收集每个内部列表的结果,以及
  • 正在收集外部列表的结果
  • 调用堆栈的这些用途之一可以在累积参数中转换为显式堆栈。这就是如何定义尾部递归
    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
    

    您是否有任何类型的代码显示您失败的尝试?你具体在哪里遇到了困难?很抱歉回答得太晚,这里是上面的一个尝试。回答得好。虽然
    map
    map嵌套
    不需要与
    连接。只需将
    map
    放在第一位。
    mapNested f=map(map f)
    虽然对于大型列表不会导致
    StackOverflowException
    ,但它仍然是
    map
    的嵌套调用,不是吗?非常感谢!这正是我想要的答案!
    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