저는 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
- 답변 # 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"
관련 자료
- 파이썬에서 정규식 (refindall)을 사용하여 텍스트에서 15 자리 문자열 추출
- java - 정규식을 사용하여 문자열을 구문 분석하는 방법
- json - jq를 사용하여 배열을 구문 분석하고 문자열에 매핑
- C를 사용하여 파일의 문자열 부분을 선택적으로 읽기
- shell - jq를 사용하여 문자열 변수를 인수로 사용하여 json 만들기
- python - split 메서드를 사용하지 않고 문자열에서 가장 긴 단어를 찾는 방법
- python - startswith 및 index를 사용하여 구조화 된 문자열에서 하위 문자열 찾기
- php - openssl_decrypt를 사용하여 암호화 된 문자열을 해독하는 방법
- awk - SED 명령을 사용하여 특정 문자열을 가져 오는 방법
- shell - Linux 명령을 사용하여 문자열의 시작과 끝에서 표현식을 건너 뛰는 방법
- c# - 문자열을 사용하여 다른 클래스의 두 숫자 계산
- java - 중첩 된 루프를 사용하여 다른 문자열에서 문자열 발생
- linux - Bash/Shell 스크립팅에서 Regex를 사용하여 특정 문자열 검색
- python - 와일드 카드를 사용하여 특정 문자열에 대한 DataFrame을 검색하는 방법
- 문자열 벡터를 사용하는 동안 C ++의 세분화 오류
- apache - htaccess를 사용하여 쿼리 문자열을 파일 확장자로 교체
- linq - automapper를 사용하여 쉼표로 구분 된 문자열 또는 정수를 IEnumerable 에 매핑하는 방법
- java - 정규식을 사용하여이 문자열을 일치시키는 방법
- bash, 문자열 사용을위한 스크립트
- 파이썬을 사용하여 문자열 '5a'를 '5b'로 증가시키는 방법
- OpenCv의 폴더에서 여러 이미지 읽기 (python)
- 파이썬 셀레늄 모든 "href"속성 가져 오기
- html - 자바 스크립트 - 클릭 후 변경 버튼 텍스트 변경
- javascript - 현재 URL에서 특정 div 만 새로 고침/새로 고침
- JSP에 대한 클래스를 컴파일 할 수 없습니다
- JavaScript 변수를 HTML div에 '출력'하는 방법
- git commit - 자식 - 로컬 커밋 된 파일에 대한 변경을 취소하는 방법
- jquery - JavaScript로 현재 세션 값을 얻으시겠습니까?
- javascript - swiperjs에서 정지, 재생 버튼 추가
- python - 화면에서 찾은 요소를 찾을 수없는 경우 셀레늄
Map
주문됩니다. 이것이Map k v
에서 작동하는 대부분의 기능입니다. 와이즈 비즈 강제. 내부적으로는 일종의 이진 트리로 유지된다고 생각합니다. 실행 순서는 없으며 데이터 순서 만 바꿉니다. 정렬되지 않고 원하는 순서대로 "맵형"구조를 원하면 연관 목록을 사용하십시오.type Assoc k v = [(k, v)] -- usually we don't use this alias; [(k, v)] is shorter and widely understood
에서 을 찾을 수 있습니다및 모든 다른지도와 유사한 작업은 기본 목록 조작을 통해 구현할 수 있습니다.
코드에서 다음과 같이 변경해야합니다 :
<시간 />또한
type ItemRegistry = [(String, InputLine)]
를 사용하면 목록에서 목록으로 일부 기능을 쓰려면 일반적으로 순서를 반대로합니다.나머지 목록으로 나머지 작업을하기 전에 마지막 요소로 작업을한다고 말하는 반면
는 나머지를 처리하기 전에 첫 번째 요소로 무언가를 수행하는 것을 의미합니다. 언어를 엄격하게 사용하는 경우에는 역순입니다. 게으른 언어에서, 표현 "happen"의 "외부"에있는 것들이 먼저;엄격한 언어로 '내부'에 관한 일이 먼저 발생합니다.
이것은
<시간 />foldr c n [x, y, z] = c x (foldr c n [x, y])
에게는 문제가되지 않았습니다Map
때문에 (중복 키가없는 한) 순서가 없습니다.Map.insert
에 키를 삽입하는 순서는 중요하지 않습니다.Map
때문에 항상Map
에 따라 정렬됩니다 예. 그러나 순서를 유지하는 연결 목록의 문제입니다. 따라서 다음과 같이 말해야합니다.마침내 당신의
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 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
가됩니다. 연결 목록에 추가합니다.그리고 당신은 말할 것입니다
(이것이 비효율적으로 보인다고 생각하면 그렇지 않습니다.
writeData path data_ = [ writeLine path k v | (k, v) <- assocs data_ ]
에 대한 문서 그것은 목록 퓨전에 영향을받는다고 말합니다. 여기에 생성 된 것으로 보이는 목록은 존재하지 않게 최적화됩니다.)물론, 우리는 이미 연관리스트를가지고있다. 이것은
assocs
를 만든다 더 간단합니다 :