从Haskell数据库获取列值

从Haskell数据库获取列值

问题描述:

问题出在这里。让我们知道,我对Haskell非常陌生,声明性语言部分与我以前的完全不同。我创建了一个排序数据库,用户可以输入命令如“添加(用户”名称“)”或“创建(表格”资助“)”。我试图创建一个函数,它将参数作为一个命令列表,一个用户,一个表,一个列名(作为一个字符串),并返回一个包含该列中的值的列表,如果用户有权访问它们即某处命令列表中有一个符合“允许(用户)(表‘基金’)”。我们可以假设表中存在。从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]像它应该那样,但是用替换不起作用。

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” 或 “运行” 的功能,其具有类型'EVAL :: [命令] - >([(表,[(柱,整数)])],[(用户,数据表)] ',这里的元组的第一个元素是它们的内容表的列表,第二个是“允许”的关系(即允许哪些用户的数据库)。当然,你可以(并且可能应该)定义自己的数据类型。一旦你有了,你的问题变得相当微不足道。 – user2407038 2014-11-24 00:19:29

在你的代码中有几件奇怪的事情;例如:

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声明,其中明确告诉哈斯克尔扔掉元组的第二个值_。相反,你想要的东西,这是抓住与表相关联的所有(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我上面写的未tupled版)。有了这个算法变得更容易:

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)对所有在表中的行/的cols的,我们有拉出只是我们需要列一个非常简单的时间。

TODO:当有人说Insert (Table "Revenue") [("Amount", 1), ("Amount", 2400)],这将出现在输出,即使它只是来自一个行两行停止做某事意想不到的验证码。您可以正常化上投入,这将做的相当好,或返回[Maybe Integer],给空对不具有值(标准前奏lookup将采取concat的地方做你的工作你)的行。