从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'
可以包含任意数量的元组的列表,但必须检查列名是否首先匹配。
任何帮助都将不胜感激。
在你的代码中有几件奇怪的事情;例如:
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
的地方做你的工作你)的行。
你的数据类型是很好的在“自然”的方式写入命令,但不容易做事情你正在做的类型。我建议先写一个 “EVAL” 或 “运行” 的功能,其具有类型'EVAL :: [命令] - >([(表,[(柱,整数)])],[(用户,数据表)] ',这里的元组的第一个元素是它们的内容表的列表,第二个是“允许”的关系(即允许哪些用户的数据库)。当然,你可以(并且可能应该)定义自己的数据类型。一旦你有了,你的问题变得相当微不足道。 – user2407038 2014-11-24 00:19:29