【问题标题】:Transpose in Elm without Maybe在没有 Maybe 的情况下在 Elm 中转置
【发布时间】:2015-08-11 03:23:37
【问题描述】:

我有一个整数列表[[1,2,3,4],[1,2,3,4]]

我想把它转换成[[1,1],[2,2],[3,3]...]

我有:

transpose : List (List a) -> List (List a)
transpose ll = case ll of
    ((x::xs)::xss) -> (x :: (List.map List.head xss)) :: transpose (xs :: (List.map List.tail xss))
    otherwise -> []

但问题是编译器不喜欢 head 和 tail 操作并且想要返回 Maybe 类型。

如何在 elm 中正确转置列表?

【问题讨论】:

    标签: list functional-programming transpose elm


    【解决方案1】:

    这取决于...
    考虑到所有边缘情况,您想彻底做到这一点吗?还是以快速而肮脏的方式进行?我所说的边缘情况是子列表具有不同长度的列表列表。

    非常肮脏的方式

    在极端情况下,您会遇到程序崩溃

    unsafeHead l =
      case l of
        (h :: t) -> h
        _ -> Debug.crash "unsafeHead called with empty list"
    
    unsafeTail l =
      case l of
        (h :: t) -> t
        _ -> Debug.crash "unsafeTail called with empty list"
    
    transpose ll =
      case ll of
        ((x::xs)::xss) ->
          let
            heads = 
              List.map unsafeHead xss
    
            tails =
              List.map unsafeTail xss
          in
            (x :: heads) :: transpose (xs :: tails)
    
        _ ->
          []
    

    祝那些随机程序崩溃的好运,别说我没有警告你!

    不要关心边缘情况(或者:较短的行也可以)

    在极端情况下,您会得到:transpose [[10,11],[20],[],[30,31,32]] == [[10,20,30],[11,31],[32]]

    transpose ll =
      case ll of
        [] ->
          []
    
        ([] :: xss) ->
          transpose xss
    
        ((x::xs) :: xss) ->
          let
            heads =
              List.filterMap List.head xss
    
            tails =
              List.filterMap List.tail xss
          in
            (x :: heads) :: transpose (xs :: tails)
    

    拥抱Maybe

    在极端情况下,您会收到 Nothing

    如果您想要在拥有所有子列表大小相同的列表列表时进行转置,则只需提升从与 List.head 映射得到的 Maybes /List.tail:

    transpose : List (List a) -> Maybe (List (List a))
    transpose ll =
      case ll of
        ((x::xs)::xss) ->
          let
            heads =
              xss
              |> List.map List.head
              |> insideout
    
            tails =
              xss
              |> List.map List.tail
              |> insideout
          in
            (x #^ heads) ^#^ ((xs #^ tails) `Maybe.andThen` transpose)
    
        _ ->
          if ll == List.filter List.isEmpty ll then
            Just []
          else
            Nothing
    
    ----- Some helper functions: -----
    
    
    mCons : a -> Maybe (List a) -> Maybe (List a)
    mCons v ml = Maybe.map ((::) v) ml
    
    v #^ ml = mCons v ml
    
    -- this is really a Maybe.map2 (::) mv ml
    -- but the standard library doesn't provide map2 :(
    m2Cons : Maybe a -> Maybe (List a) -> Maybe (List a)
    m2Cons mv ml =
      case (mv,ml) of
        (Just v, Just l) -> Just (v :: l)
        _ -> Nothing
    
    mv ^#^ ml = m2Cons mv ml
    
    -- list of justs to just of list
    insideout : List (Maybe a) -> Maybe (List a)
    insideout l =
      case l of
        [] -> Just []
        ((Just v) :: tail) -> v #^ insideout tail
        (Nothing :: _) -> Nothing
    

    【讨论】:

      【解决方案2】:

      您有时可以使用List.take 1/List.drop 1 代替List.head/List.tail,以防使用空的List 而不是Nothing 更有意义。

      transpose 的例子中,如果你想这样写,当列表长度不相等时它会丢弃任何额外的值(即根据最短的列表只“尽可能多地”转置) ,你可以使用:

      transpose : List (List a) -> List (List a)
      transpose ll =
        let heads = List.map (List.take 1) ll |> List.concat
            tails = List.map (List.drop 1) ll 
        in
            if | List.length heads == List.length ll ->
                   heads::(transpose tails)
               | otherwise ->
                   []
      
      transpose [[1,2,3,4],[1,2,3,4]]        --> [[1,1],[2,2],[3,3],[4,4]]
      transpose [[10,11],[20],[],[30,31,32]] --> []
      

      如果您希望它继续从列表中取出直到它们全部消失(即根据最长的列表“尽可能多地”转置),您可以使用:

      transpose : List (List a) -> List (List a)
      transpose ll =
        let heads = List.map (List.take 1) ll |> List.concat
            tails = List.map (List.drop 1) ll 
        in
            if | List.isEmpty heads ->
                   []
               | otherwise ->
                   heads::(transpose tails)
      
      transpose [[1,2,3,4],[1,2,3,4]]        --> [[1,1],[2,2],[3,3],[4,4]]
      transpose [[10,11],[20],[],[30,31,32]] --> [[10,20,30],[11,31],[32]]
      

      在矩阵格式良好的情况下,两者都同样有效,因此如果您想检查边缘情况并执行其他操作,您可以先执行此操作。他们只是处理边缘情况略有不同。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-04-11
        • 2023-03-09
        • 2021-10-08
        • 2018-02-05
        • 2023-03-08
        • 2017-12-02
        相关资源
        最近更新 更多