Haskell - 在列表中相互比较不同的项目

时间:2014-11-23 21:39:00

标签: haskell recursion

新的haskell(或一般的函数式编程语言),所以我仍然很难将我脑海中的逻辑转换为实际的代码。

鉴于以下内容

data User = User String deriving (Eq, Show)
data Task = Task String deriving (Eq, Show)
data Command =
    Add User
  | Create Task
  | Allow (User, Task)
  deriving (Eq, Show)

我想确保每个Allow都有一个已添加的用户。上述逻辑将在函数Verified :: [Command] - >中定义。布尔

example = [
    Add (User "Michael"),
    Create (Task "Laundry"),
    Allow (User "Michael", Task "Laundry")
  ]

Verified example -- Return True

example2 = [
    Add (User "Michael"),
    Create (Task "Laundry"),
    Allow (User "Bob", Task "Laundry")
  ]

Verified example2 -- Return False

以下是我的思考过程,如果你能帮助我将其翻译成代码/改进我的想法,我会非常感激

  1. 反转命令列表。如果我看到一个允许用户和任务,那么在列表的某个地方必须是真的,有一个添加与该特定用户。否则,返回false。

  2. 在Prelude中使用elem来查看命令中是否存在这样的Add(我在考虑递归)。

  3. 现在我只能成功创建一个反转列表的简单函数,

    reverseList :: [Command] -> [Command]
    reverseList [] = []
    reverseList (x:xs) = (reverse xs) ++ [x]
    

    但除此之外,我真的不知道从哪里开始实施Verified :: [Command] - >布尔

    编辑:刚发现Haskell有一个内置的反向函数,猜测我的reverseList将不再使用了

2 个答案:

答案 0 :(得分:1)

我的理解是命令列表表示依赖于每个先前命令有效或无效的序列。也就是说,[Add "Bob", Allow "Bob" ...]有效,而[Allow "Bob" ..., Add "Bob"]

在这种情况下,执行此操作的最简单方法可能是反转列表并迭代它;如果找到Allow,请将其插入您必须找到的Add集合中;如果您找到Add,请将其从必须找到的Add集中删除。然后,一旦到达空列表,如果您的集合不为空,您就会发现命令序列无效。

您可以使用列表来表示集合,但使用Data.Set.Set会更容易,import qualified Data.Set as S verify :: [Command] -> Bool verify = go S.empty . reverse where go :: S.Set User -> [Command] -> Bool go s [] = error "TODO" go s (x:xs) = case x of Create {} -> go s xs Add u -> error "TODO" Allow (u, _) -> error "TODO" 已经定义了插入和删除(称为删除)函数。

以下是一些可以帮助您入门的代码:

Ord

你可以使用折叠,但使用原始递归编写它可能很有启发性。

要实现此目的,您还需要为User派生 newtype User = User String deriving (Eq, Show, Ord)

Scroll right >                                                                                                                                                                                          go :: S.Set User -> [Command] -> Bool
                                                                                                                                                                                                        go s [] = S.null s 
                                                                                                                                                                                                        go s (x:xs) = case x of 
                                                                                                                                                                                                                        Create {}    -> go s xs 
                                                                                                                                                                                                                        Add    u     -> go (S.delete u s) xs 
                                                                                                                                                                                                                        Allow (u, _) -> go (S.insert u s) xs 

解决方案(ps给我剧透标签堆栈溢出):

{{1}}

答案 1 :(得分:1)

这个怎么样:

首先从reverse导入Data.List

import Data.List (reverse)

定义verify以在命令列表的反面应用verifyBackwards

verify :: [Command] -> Bool
verify cmds = verifyBackwards (reverse cmds)

verifyBackwards然后会为每个元素调用verifyElem以及反向命令列表的其余部分(即原始列表中该元素之前的元素)and将结果与递归调用verifyBackwards以验证其余元素:

verifyBackwards :: [Command] -> Bool
verifyBackwards [] = True
verifyBackwards (x:xs) = verifyElem x xs && verifyBackwards xs

最后verifyElem将为除True命令之外的所有命令返回Allow。对于Allow命令,它会在Add编辑之前检查用户是否为Allow

verifyElem :: Command -> [Command] -> Bool
verifyElem (Allow (User u, _)) xs = elem (Add (User u)) xs
verifyElem _ _ = True