这是问题所在。让我们知道我对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'
可以接收任意数量的元组列表,但必须先检查列名是否匹配。
非常感谢任何帮助。
答案 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}}为你工作)。