“Alternative”中的模式匹配

2024-04-23

我有一个函数,它的参数进行模式匹配以生成计算StateT () Maybe ()。可以这么说,此计算在运行时可能会失败,在这种情况下,我希望当前的模式匹配分支失败。

我非常怀疑是否有可能有类似的东西

compute :: Int -> StateT () Maybe Int
compute = return

f :: Maybe Int -> Maybe Int -> StateT () Maybe ()
f (Just n1) (Just n2) = do
  m <- compute (n1 + n2) 
  guard (m == 42)
f (Just n) _ = do
  m <- compute n
  guard (m == 42)
f _ (Just n) = do
  m <- compute n
  guard (m == 42)

按照我想要的方式行事:当第一次计算由于以下原因失败时guard或在某个地方compute, 我想f尝试下一个模式。

显然上面的方法是行不通的,因为StateT(就像任何其他单子一样)在扩展时会涉及一个附加参数,所以我可能无法将其表述为简单的模式保护。

以下是我想要的,但它很丑陋:

f' :: Maybe Int -> Maybe Int -> StateT () Maybe ()
f' a b = asum (map (\f -> f a b) [f1, f2, f3])
  where
    f1 a b = do
      Just n1 <- pure a
      Just n2 <- pure b
      m <- compute (n1 + n2) 
      guard (m == 42)
    f2 a _ = do
      Just n <- pure a
      m <- compute n
      guard (m == 42)
    f3 _ b = do
      Just n <- pure b
      m <- compute n
      guard (m == 42)

像这样的电话execStateT (f (Just 42) (Just 1)) ()会失败f但返回Just () for f',因为它匹配f2.

我如何获得的行为f'同时具有优雅的模式匹配和尽可能少的辅助定义,如f?还有其他更优雅的方式来表达这个吗?


完整的可运行示例:

#! /usr/bin/env stack
-- stack --resolver=lts-11.1 script

import Control.Monad.Trans.State
import Control.Applicative
import Control.Monad
import Data.Foldable

compute :: Int -> StateT () Maybe Int
compute = return

f :: Maybe Int -> Maybe Int -> StateT () Maybe ()
f (Just n1) (Just n2) = do
  m <- compute (n1 + n2) 
  guard (m == 42)
f (Just n) _ = do
  m <- compute n
  guard (m == 42)
f _ (Just n) = do
  m <- compute n
  guard (m == 42)

f' :: Maybe Int -> Maybe Int -> StateT () Maybe ()
f' a b = asum (map (\f -> f a b) [f1, f2, f3])
  where
    f1 a b = do
      Just n1 <- pure a
      Just n2 <- pure b
      m <- compute (n1 + n2) 
      guard (m == 42)
    f2 a _ = do
      Just n <- pure a
      m <- compute n
      guard (m == 42)
    f3 _ b = do
      Just n <- pure b
      m <- compute n
      guard (m == 42)

main = do
  print $ execStateT (f (Just 42) (Just 1)) ()  -- Nothing
  print $ execStateT (f' (Just 42) (Just 1)) () -- Just (), because `f2` succeeded

Edit:到目前为止,我对这个问题得到了一些聪明的答案,谢谢!不幸的是,它们大多会过度拟合我给出的特定代码示例。实际上,我需要这样的东西来统一两个表达式(准确地说是let绑定),如果可能的话,我想尝试统一两个同时let的RHS,并陷入我在一侧处理let绑定的情况一次让它们漂浮。所以,实际上并没有什么巧妙的结构Maybe可以利用的论点,但我不是computeing on Int实际上。

到目前为止的答案可能会让其他人受益,超出他们给我带来的启发,所以谢谢!


Edit 2:以下是一些可能具有虚假语义的编译示例代码:

module Unify (unify) where

import Control.Applicative
import Control.Monad.Trans.State.Strict

data Expr
  = Var String -- meta, free an bound vars
  | Let String Expr Expr
  -- ... more cases
  -- no Eq instance, fwiw

-- | If the two terms unify, return the most general unifier, e.g.
-- a substitution (`Map`) of meta variables for terms as association
-- list.
unify :: [String] -> Expr -> Expr -> Maybe [(String, Expr)]
unify metaVars l r = execStateT (go [] [] l r) [] -- threads the current substitution as state
  where
    go locals floats (Var x) (Var y)
      | x == y = return ()
    go locals floats (Var x) (Var y)
      | lookup x locals == Just y = return ()
    go locals floats (Var x) e
      | x `elem` metaVars = tryAddSubstitution locals floats x e
    go locals floats e (Var y)
      | y `elem` metaVars = tryAddSubstitution locals floats y e
    -- case in point:
    go locals floats (Let x lrhs lbody) (Let y rrhs rbody) = do
      go locals floats lrhs rrhs -- try this one, fail current pattern branch if rhss don't unify
      -- if we get past the last statement, commit to this branch, no matter
      -- the next statement fails or not
      go ((x,y):locals) floats lbody rbody
    -- try to float the let binding. terms mentioning a floated var might still
    -- unify with a meta var
    go locals floats (Let x rhs body) e = do
      go locals (Left (x,rhs):floats) body e
    go locals floats e (Let y rhs body) = do
      go locals (Right (y,rhs):floats) body e

    go _ _ _ _ = empty

    tryAddSubstitution = undefined -- magic

当我需要这样的东西时,我只是使用asum与内嵌的块。这里我也浓缩了多种模式Just n1 <- pure a; Just n2 <- pure b合而为一,(Just n1, Just n2) <- pure (a, b).

f :: Maybe Int -> Maybe Int -> StateT () Maybe ()
f a b = asum

  [ do
    (Just n1, Just n2) <- pure (a, b)
    m <- compute (n1 + n2) 
    guard (m == 42)

  , do
    Just n <- pure a
    m <- compute n
    guard (m == 42)

  , do
    Just n <- pure b
    m <- compute n
    guard (m == 42)

  ]

您还可以使用链<|>, 如果你更喜欢:

f :: Maybe Int -> Maybe Int -> StateT () Maybe ()
f a b

  = do
    (Just n1, Just n2) <- pure (a, b)
    m <- compute (n1 + n2) 
    guard (m == 42)

  <|> do
    Just n <- pure a
    m <- compute n
    guard (m == 42)

  <|> do
    Just n <- pure b
    m <- compute n
    guard (m == 42)

对于这种“失败”来说,这大约是最小的。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

“Alternative”中的模式匹配 的相关文章

  • Scala 中的 Reader monad:返回、本地和序列

    我正在使用ReaderScala 中的 monad 由scalaz https github com scalaz scalaz图书馆 我对这个单子很熟悉正如 Haskell 中定义的 https wiki haskell org All
  • 功能段落

    抱歉 我还不太明白 FP 我想将一系列行分割成一系列行序列 假设一个空行作为段落划分 我可以在 python 中这样做 如下所示 def get paraghraps lines paragraphs paragraph for line
  • Haskell FFI - 你能从 Haskell 数据结构中获取 C 指针吗?

    我有很多 C 结构体 结构如下 typedef struct unsigned int a unsigned int b StructA 还有很多功能 比如 void doSomethingWith StructA StructB Stru
  • macOS 上的堆栈构建

    我是新来的haskell 我有最简单的程序 usr bin env stack stack resolver lts 13 7 script module Main where import Lib main IO main putStrL
  • java中使用[:punct:]函数的正则表达式

    我在用 punct 替换 a 中特殊字符的函数 字符串例如 REPLACE REGEXP REPLACE 第 1 列 punct 作为输出 作为java中SQL字符串的一部分 但我想要特定的特殊字符 不被替换 你能建议最好的方法吗 Acc
  • ? LIKE(列 || '%')

    我可以有这样的条件吗 SELECT FROM table WHERE LIKE column 哪里的 是一个字符串参数值 例如 这些参数值 当列等于时应返回 true admin products admin products 1 admi
  • 理解 Haskell 中的矩阵转置函数

    这个矩阵转置函数有效 但我试图理解它的逐步执行 但我不明白 transpose a gt a transpose transpose x map head x transpose map tail x with transpose 1 2
  • 按广度优先顺序列出目录所有内容导致效率低下

    我编写了一个 Haskell 模块来按广度优先顺序列出目录的所有内容 下面是源代码 module DirElements dirElem where import System Directory getDirectoryContents
  • 从 createProcess 外部获取的句柄读取

    我正在尝试创建一个进程 并通过我在外部提供的句柄与其进行通信createProcess功能 stdOutH lt openFile logDir gt stdout log ReadWriteMode hSetBuffering stdOu
  • Haskell Servant 和流媒体

    我正在尝试添加一个功能到我的servant服务器将从 Amazon S3 获取文件并将其流式传输回用户 由于文件可能很大 我不想将它们下载到本地然后将它们提供给客户端 我宁愿将它们直接从 S3 流式传输到客户端 I use Amazonka
  • 将相同变量绑定到共享特征的不同类型的模式

    我有一个关于通过特征共享某些行为的值的模式匹配的问题 我有一个带有两个变体的枚举 每个变体都有不同类型的绑定值 其中两种类型都实现一个特征 我试图弄清楚是否可以创建一个单一的模式 E VarA x E VarB x 形式 其中我将两种类型绑
  • 如何在 Windows 7 中配置 cabal?

    我已经在Windows 7中安装了Haskell Platform 2012 我在控制台中编写cabal update我收到消息说有新版本的阴谋集团 我写的cabal install cabal install 安装完成后 它告诉我 cab
  • 函数式编程是否需要新的命名约定?

    我最近开始使用 Haskell 学习函数式编程 并在 Haskell 官方 wiki 上发现了这篇文章 如何阅读哈斯克尔 http www haskell org haskellwiki How to read Haskell What t
  • 用parsec解析递归数据

    import Data Attoparsec Text Lazy import Data Text Lazy Internal Text import Data Text Lazy pack data List a Nil Cons a L
  • 如何使用 Haskell 中的 thyme 库从 Int 值创建 UTCTime?

    我有年 月 日 小时和分钟值 所有这些都是类型Int 我怎样才能将它们转换为UTCTime or UniversalTime 需要导入以下内容 import Control Lens import Data Thyme Clock impo
  • PostgreSQL 对 string\varchar 的各种清理

    我必须通过以下方式清理一些 varchar 删除特殊字符 例如 来自封闭列表 我已经成功地通过大量使用replace regexp replace来做到这一点 但我正在寻找类似于SQL Server中的东西 删除以下数字但不删除相邻的数字含
  • 如何在 Yesod 中使用 CSS 框架?

    我想将 Blueprint CSS 框架与 Yesod 一起使用 有没有最佳实践 因为 Yesod 使用 CSS 模板 所以在我看来我不能直接使用 css 文件 我必须将它们重命名为 lucius files 吗 如何将 CSS 添加到 d
  • Haskell Cabal 包 - 找不到 Paths_ 模块

    我正在开发一个 Haskell 项目 Happstack 服务器 Blaze HTML 前端作为主要库 我想添加一个静态数据目录 看起来你可以使用 Cabal 使用自动生成的Path
  • 访问函数中的环境

    In main我可以读取我的配置文件 并将其提供为runReader somefunc myEnv正好 但somefunc不需要访问myEnv读者提供 链中的下一对也没有提供 需要 myEnv 中某些内容的函数是一个微小的叶函数 如何在不将
  • Haskell:找不到模块“Data.List.Split”

    我正在尝试在 Haskell 中拆分列表 据我所知 最简单的方法是splitOn 但是这个函数需要Data List Split 所以我尝试运行import Data List Split在前奏曲中 但是 我收到以下错误 Could not

随机推荐