>

저는 haskell의 초보자이며 첫 번째 작은 프로그램을 작성하기 시작합니다.

목적 : 앱은 사용자에게 일부 카테고리의 항목을 입력하도록 요청합니다. 모든 입력은 맵 카테고리 [Item] (또는 맵 문자열 [String])에 저장됩니다. 모든 카테고리가 충족되면지도가 파일에 기록됩니다.

import qualified Data.Map as Map
type InputLine = IO [String]
type ItemsRegistry = Map.Map String InputLine
--main = do sequence . printValues . getInput $ ["head", "body"]
main = do
  writeFile "data.txt" ""
  putStrLn "Write the word \"stop\" to end the input"
  sequence . writeData "data.txt" . getInput $ ["B", "A"]
getInput :: [String] -> ItemsRegistry
getInput cat = foldl (\acc category -> Map.insert category (insertCategory category) acc) Map.empty cat
insertCategory :: String -> InputLine
insertCategory category = do
  putStrLn $ "Add items for category " ++ category
  insertItem []
insertItem :: [String] -> InputLine
insertItem values = do
  x <- getLine
  case x of "stop" -> return values
            x -> insertItem (x:values)
writeData ::  String -> ItemsRegistry -> [IO ()]
writeData path data_ = [ writeLine path k (Map.lookup k data_) |  k <- Map.keys data_ ]
-- filepath category lineOfItems
writeLine :: String -> String -> Maybe InputLine -> IO ()
writeLine path category (Just line) = line >>= (\words -> appendFile path $ formatLine category (unwords words))  
writeLine path category Nothing = return ()
formatLine :: String -> String -> String
formatLine category items = category ++ " " ++ items ++ "\n"
--printValues :: ItemsRegistry -> [IO ()]
--printValues l = [ v >>= putStrLn . show | (k,v) <- Map.toList l]

위의 코드를 실행할 때 foldl을 사용했기 때문에 B보다 먼저 입력 카테고리 A를 묻는 이유를 알 수 없습니다. 그 뒤에 어휘 순서가있는 것 같지만 이유를 이해할 수 없습니다.

감사합니다

  • 답변 # 1

    Map 주문됩니다. 이것이 Map k v 에서 작동하는 대부분의 기능입니다. 와이즈 비즈  강제. 내부적으로는 일종의 이진 트리로 유지된다고 생각합니다. 실행 순서는 없으며 데이터 순서 만 바꿉니다. 정렬되지 않고 원하는 순서대로 "맵형"구조를 원하면 연관 목록을 사용하십시오.

    Ord k
    
    

    type Assoc k v = [(k, v)] -- usually we don't use this alias; [(k, v)] is shorter and widely understood 에서 을 찾을 수 있습니다

    Prelude
    
    

    및 모든 다른지도와 유사한 작업은 기본 목록 조작을 통해 구현할 수 있습니다.

    코드에서 다음과 같이 변경해야합니다 :

    lookup :: Eq a => k -> [(k, v)] -> Maybe v
    
    

    <시간 />

    또한 type ItemRegistry = [(String, InputLine)] 를 사용하면  목록에서 목록으로 일부 기능을 쓰려면 일반적으로 순서를 반대로합니다.

    foldl
    
    

    나머지 목록으로 나머지 작업을하기 전에 마지막 요소로 작업을한다고 말하는 반면

    foldl c n [x, y, z] = c (foldl c n [x, y]) z
    
    

    는 나머지를 처리하기 전에 첫 번째 요소로 무언가를 수행하는 것을 의미합니다. 언어를 엄격하게 사용하는 경우에는 역순입니다. 게으른 언어에서, 표현 "happen"의 "외부"에있는 것들이 먼저;엄격한 언어로 '내부'에 관한 일이 먼저 발생합니다.

    이것은 foldr c n [x, y, z] = c x (foldr c n [x, y]) 에게는 문제가되지 않았습니다 Map 때문에  (중복 키가없는 한) 순서가 없습니다. Map.insert 에 키를 삽입하는 순서는 중요하지 않습니다. Map 때문에  항상 Map 에 따라 정렬됩니다  예. 그러나 순서를 유지하는 연결 목록의 문제입니다. 따라서 다음과 같이 말해야합니다.

    Ord
    
    

    <시간 />

    마침내 당신의 getInput :: [String] -> ItemsRegistry -- getInput = foldr (\category acc -> (category, insertCategory category) : acc) [] -- but that should really be getInput = map (\category -> (category, insertCategory category)) -- which you can turn into -- getInput = map ((,) <*> insertCategory) -- if you want  그리고 writeLine  의심 스럽다. 통지 :

    writeData
    
    

    writeData path data_ = [ writeLine path k (Map.lookup k data_) | k <- Map.keys data_ ]  확실히 k 의 열쇠입니다 그러나 data_ . 왜? 그것은 Map.lookup k data_ :: Maybe InputLine 되지 않습니다 . Nothing 도 마찬가지입니다. . 이것은 당신이 잘못한 것을 의미하며, 당신이 그것을 보았을 때 당신을 불편하게 만들 것이라고 확신합니다. Data.List.lookup 를 만들어서 반창고를 보았습니다   writeLine 를 수락 Maybe 를 수신하면 작동하지 않습니다. , 그러나 그것은 단지 dodgy입니다.

    Nothing 를 사용중인 경우  (실제로 말했듯이)이 기능을 사용하면 Map 가됩니다.  연결 목록에 추가합니다.

    Map
    
    

    그리고 당신은 말할 것입니다

    -- alias toAscList
    Data.Map.assocs :: Map k a -> [(k, a)]
    
    

    (이것이 비효율적으로 보인다고 생각하면 그렇지 않습니다. writeData path data_ = [ writeLine path k v | (k, v) <- assocs data_ ] 에 대한 문서  그것은 목록 퓨전에 영향을받는다고 말합니다.  여기에 생성 된 것으로 보이는 목록은 존재하지 않게 최적화됩니다.)

    물론, 우리는 이미 연관리스트를가지고있다. 이것은 assocs 를 만든다  더 간단합니다 :

    assocs data_
    
    

  • 답변 # 2

    Data.Map, writeData 문서에 따르면  "지도의 모든 키를 오름차순으로 반환합니다." ( -- do be careful; Map.insert clobbers duplicates, but (:)ing onto an association -- list means duplicate keys are preserved -- you'd need to run nubBy ((==) `on` fst) on data if that's an issue -- *that* might be inefficient (O(n^2) in the length of data_) writeData path data_ = [ writeLine path k v | (k, v) <- data_ ] -- or even -- writeData path = map (uncurry (writeLine path)) -- if you want to be confusing -- writeData = map . uncurry . writeLine writeLine :: String -> String -> InputLine -> IO () writeLine path category lines = do words <- lines appendFile path $ formatLine category $ unwords words 는  type의 키는 Map.keys 의 인스턴스 인 유형이어야합니다. .)

    와이즈 비즈 이후   Map 전에 온다  당연히 Ord 를 사용한다고 생각합니다.   "A" 를 일으키는  "B에 대한 요청"전에 "A에 대한 요청"작업을 수행합니다.

    "B"

관련 자료

  • 이전 c++ - 사용자 정의 객체 포인터의 메모리 관리를위한 안전한 래퍼 클래스 작성
  • 다음 unix - doc 및 docx 파일에서 일반 텍스트를 추출하는 방법은 무엇입니까?