从Haskell数据库中获取列值

时间:2014-11-23 21:55:29

标签: database list haskell pattern-matching

这是问题所在。让我们知道我对Haskell很新,声明性语言部分与我以前完​​全不同。我已经创建了一个类别的数据库,用户可以输入“添加(用户”名称“)”或“创建(表”资金“)”等命令。我正在尝试创建一个函数,该函数将命令列表,用户,表,列名称(作为字符串)作为参数,并返回包含该列中的值的列表(如果用户有权访问它们)(即命令列表中的某个地方有一个匹配“允许(用户名称)(表”基金“)”。我们可以假设该表存在。

    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替换为Amount不起作用。

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]返回仅与(Column,Integer)元组列表中的第一个元组匹配的元组列表。

lookupColumn接受元组列表并返回其中的整数列表。与lookup'不同,我们知道它所接收的列表中只包含正确的列(Column,Integer)元组。 lookup'可以接收任意数量的元组列表,但必须先检查列名是否匹配。

非常感谢任何帮助。

1 个答案:

答案 0 :(得分: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,它明确告诉Haskell丢弃元组的第二个值。相反,您需要一些能够抓取与表关联的所有(Column, Integer)对的东西:

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的非拼写版本)。有了这个,算法就变得容易了:

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

这里的大部分“工作”是做什么的?实际上它只是我们在concat :: [[a]] -> [a]中使用的getCells。通过将表中所有行/列的所有(Column, Integer)对连接在一起,我们很容易只抽出我们需要的列。

Todo:当有人说Insert (Table "Revenue") [("Amount", 1), ("Amount", 2400)]时,阻止此代码执行意外操作,即使它只来自一行,它会在输出中显示为两行。您可以标准化输入,这样做会很好,或者返回[Maybe Integer],为没有值的行提供空值(标准Prelude中的lookup将取代{ {1}}为你工作)。