使用Elm中的Stack ADT反转字符串

时间:2016-04-30 07:10:29

标签: stack reverse elm

import Html exposing (..)
import String

type alias Stack = List String


push : String -> Stack -> Stack
push tok stack = 
 (tok :: stack)


pop : Stack -> (Maybe String, Maybe Stack)
pop stack = 
 let 
   top = List.head stack 
 in 
 (top, List.tail stack)


reverseString: String -> String
reverseString incoming = 
 let 
   stringStack = incoming 
    |> String.split "" 
    |> List.foldl push [] 
 in 
   -- How to use pop() here?
   List.foldr String.append "" stringStack


main : Html 
main = 
 "Hello World!"
 |> reverseString
 |> toString 
 |> text 

我正在使用reversepush()尝试使用我自己的pop()字符串。我可以合并push,但无法在函数pop中使用reverseString。我在这里做错了什么?

3 个答案:

答案 0 :(得分:2)

你正试图用你的Stack ADT List.foldr,但这是作弊;如果Stack真的是一个ADT,我们应该无法利用它的列表!

List.foldr也很难匹配Stack ADT,因为它释放了它的函数参数来处理空列表,而pop函数强制我们同时处理非空和空栈的情况

如果我们想要一个Stack ADT,我们必须手动通过堆栈进行递归,而不使用List.foldr。首先,减少pop函数以更简洁地表示“空堆栈”和“非空堆栈”这两种情况将会很方便:

pop : Stack -> Maybe (String, Stack)
pop stack = 
  case stack of 
    [] -> Nothing
    x :: xs -> Just (x, xs) 

然后我们可以制定reverseString

reverseString : String -> String
reverseString string = 
  let
    loop stack = 
      case pop stack of 
        Nothing -> ""
        Just (symbol, stack') -> symbol ++ loop stack'
  in
    String.split "" string   -- Split the string into a list
    |> List.foldl push []    -- Push each letter on the stack
    |> loop                  -- Pop each letter off the stack 

直接使用列表可能更容易。然后Cons (::)是推,List.foldl是通过弹出直到空来减少堆栈的函数。

reverseString2 : String -> String
reverseString2 = 
  String.split ""        -- "String -> Stack" (implicitly pushes)
  >> List.foldl (::) []  -- "Stack -> List" (reduces the stack)
  >> String.join ""      -- "List -> String" (nothing to do with stacks)

答案 1 :(得分:1)

Elm没有在语言级别进行迭代,因此您需要使用支持迭代和映射的数据结构。在这种情况下,我认为懒惰列表可能是最好的,因为你不会通过递归来破坏堆栈。

在这种情况下,stackIterator从堆栈中生成一个惰性的字符串列表。为了得到我们想要的惰性值序列,我们需要一个可以重复应用于它自己的结果的函数,并且因为pop返回一个head,stack的元组,该函数是((mhd,tl) - > pop TL)。下一部分作为管道运行,首先拉出元组的左侧部分,第二部分承诺如果返回的堆栈顶部为Nothing则终止列表,第三部分,通过{{1将Maybe的列表转换为字符串}}

通过替换LL.foldr和惰性列表,您的堆栈上有一个适当的非递归迭代,涉及您的pop函数。

一些风格笔记:

  • 你的堆栈确实希望String成为一个类型变量。
  • 作为样式选择,您应该更喜欢将模式匹配到可能在列表中返回的函数。
  • 如果pop返回堆栈而不是堆栈,它会使代码更清晰,因为它可以通过最高结果发出空堆栈信号。
  • 我自己的榆树风格并不完美。可能有一种聪明的方法来撰写Maybe.withDefaultsnd,并不需要明确的lambda绑定。

    pop

答案 2 :(得分:0)

FWIW,我修改了Rundberget's version以使用String作为堆栈。虽然它不像其他解决方案那样通用,但它不使用List.foldrList.foldl,最终会更短。

module SStack where

import String

type alias SStack = String

empty : SStack
empty =
  ""


push : String -> SStack -> SStack
push tok stacks = 
  tok ++ stacks


pop : SStack -> Maybe (Char, SStack)
pop stacks = 
  String.uncons stacks


reverse : SStack -> SStack
reverse stack =
  case (pop stack) of 
    Nothing ->
      empty
    Just(x, r) -> 
      push  (reverse r) (String.fromChar x)

这里是驱动程序模块:

import SStack as Stack exposing (..)
import Html exposing (..)

reverseString : String -> String
reverseString str =
  Stack.reverse str 


main : Html.Html
main =
  reverseString "Hello World !" 
    |> Html.text