2014-11-23 4 views
0

여기에 문제가 있습니다. 저는 하스켈에게 아주 익숙하다는 것을 알리고 선언적 언어 부분은 제가 익숙했던 것과 완전히 다릅니다. 나는 종류의 데이터베이스를 만들었고 사용자는 "Add (User "Name ")"또는 "Create (Table"Funding ")"와 같은 명령을 입력 할 수 있습니다. 매개 변수로 사용자, 테이블, 열 이름으로 문자열 이름), 및 사용자가 그들에 대한 액세스 권한이있는 경우 해당 열 값을 포함하는 목록을 반환하는 매개 변수로 사용하는 함수를 만들려고합니다. 즉, 어딘가 명령 목록에서 "(기금. 우리는 테이블이 존재 가정 할 수 있습니다 (사용자 이름) 표") "허용"일치 하나있다. 내가 일을 입수했습니다하스켈 데이터베이스에서 열 값 가져 오기

module Database where 

type Column = String 
data User = User String deriving (Eq, Show) 
data Table = Table String deriving (Eq, Show) 
data Command = 
    Add User 
    | Create Table 
    | Allow (User, Table) 
    | Insert (Table, [(Column, Integer)]) 
    deriving (Eq, Show) 


-- Useful function for retrieving a value from a list 
-- of (label, value) pairs. 

lookup' :: Column -> [(Column, Integer)] -> Integer 
lookup' c' ((c,i):cvs) = if c == c' then i else lookup' c' cvs 

lookupColumn :: [(Column, Integer)] -> [Integer] 
lookupColumn ((c, i):cvs) = if null cvs then [i] else [i] ++ lookupColumn cvs 

select :: [Command] -> User -> Table -> Column -> Maybe [Integer] 
select a b c d = if not (elem (b, c) [(g, h) | Allow (g, h) <- a]) 
    then Nothing 
    else Just (lookupColumn [(d, x) | Insert (c, [ (d, x), _ ]) <- a]) 

하지만, 지금은 입력 형식이 표의 첫 번째 열이어야합니다. 실행 입력 : select example (User "Alice") (Table "Revenue") "Day"Just [1,2,3] 같이 반환해야하지만 Day을로 바꾸십시오.이 작동하지 않습니다.

example = [ 
    Add (User "Alice"), 
    Add (User "Bob"), 
    Create (Table "Revenue"), 
    Insert (Table "Revenue", [("Day", 1), ("Amount", 2400)]), 
    Insert (Table "Revenue", [("Day", 2), ("Amount", 1700)]), 
    Insert (Table "Revenue", [("Day", 3), ("Amount", 3100)]), 
    Allow (User "Alice", Table "Revenue") 
    ] 

기능에 대한 설명. select은 해당 열의 정수 목록을 반환해야하는 함수입니다. 지금 당장은 첫 번째 열과 만 일치하지만 사용자가 미리 원하는 열을 알지 못해서 여러 열로 작업하고 싶습니다.

[(d, x) | Insert (c, [ (d, x), _ ]) <- a]은 (열, 정수) 튜플의 각 목록에서 첫 번째 튜플 만 일치하는 튜플 목록을 반환합니다.

lookupColumn은 튜플 목록을 가져 와서 그 안에있는 정수 목록을 반환합니다. lookup'과는 달리,이 목록에는 올바른 열 (열, 정수) 튜플 만 포함됩니다. lookup'은 여러 개의 튜플 목록을 가져올 수 있지만 먼저 열 이름이 일치하는지 확인해야합니다.

아무런 도움을받지 못 하셨다면 큰 도움이 될 것입니다.

+0

데이터 유형은 "자연스러운"방법으로 명령을 작성하는 데 적합하지만 수행중인 유형의 작업을 수행하는 것은 쉽지 않습니다. 나는 eval :: [Command] -> ([Table, [(Column, Integer)])], [(User, Table)] 타입을 가진 "eval"또는 "run" ', 튜플의 첫 번째 요소는 내용이있는 테이블의 목록이고 두 번째는 관계 "허용"(즉, 어떤 사용자가 어떤 데이터베이스를 사용할 수 있는지)입니다. 물론, 그것들에 대한 자신 만의 데이터 유형. 일단 그렇게하면 문제는 아주 사소해진다. – user2407038

답변

1

코드에 몇 가지 이상한 점이 있습니다. 예를 들면 :

lookupColumn :: [(Column, Integer)] -> [Integer] 
lookupColumn ((c, i):cvs) = if null cvs then [i] else [i] ++ lookupColumn cvs 

모든 동등 이상의 방법으로 (그리고 아마도 더 빠른) map snd를 입력하는 것이 훨씬 더 길다.

자주 데이터 구조를 정의 할 때 튜플은 불필요합니다. 당신은 쓸 수 :

data Command = Add User 
      | Create Table 
      | Allow User Table 
      | Insert Table [(Column, Integer)] 
        deriving (Eq, Show) 

실제 문제가 명시 적으로 튜플의 두 번째 값을 멀리 던져 하스켈을 알려주 select 문에 _입니다.

getCells :: [Command] -> Table -> [(Column, Integer)] 
getCells db t = concat [cis | Insert t' cis <- filter isInsert db, t == t'] 
    where isInsert (Insert _ _) = True 
      isInsert _ = False 

(이것은 제가 위에서 쓴 Insert의 해제 tupled 버전을 사용하고 있습니다) 대신 당신은 테이블과 연관된 모든 (Column, Integer)쌍을 잡고 뭔가를 원한다. 이 알고리즘을 사용하면 훨씬 쉽게 알고리즘을 작성할 수 있습니다.

select :: [Command] -> User -> Table -> Column -> Maybe [Integer] 
select db user table col 
    | Allow user table `elem` db = Just [i | (c, i) <- getCells db t, col == c] 
    | otherwise = Nothing 

여기서 "작업"의 대부분은 무엇입니까? 실제로 getCells에 사용 된 것은 concat :: [[a]] -> [a]입니다.테이블의 모든 행/열에 대해 (Column, Integer) 쌍을 모두 연결하면 필요한 열만 추출하는 것이 매우 쉽습니다.

Todo :이 코드가 누군가 Insert (Table "Revenue") [("Amount", 1), ("Amount", 2400)]이라고 말하면 예기치 않은 일을하지 않도록합니다. 이는 한 행에서만 나오는 경우에도 출력에 두 행으로 표시됩니다. 정규화 할 때 입력이 정상적으로 이루어 지거나 또는 [Maybe Integer]을 반환하여 값이없는 행에 null을 제공 할 수 있습니다 (표준 서곡에서 lookup은 작업 할 때 concat을 대신 할 것입니다).

관련 문제