首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >混合和匹配州内的有状态计算

混合和匹配州内的有状态计算
EN

Stack Overflow用户
提问于 2015-11-26 10:11:51
回答 2查看 288关注 0票数 14

我的程序的状态由三个值组成,abc类型为ABC。不同的函数需要访问不同的值。我希望使用State monad编写函数,以便每个函数只能访问它需要访问的状态的各个部分。

我有以下四种功能:

代码语言:javascript
复制
f :: State (A, B, C) x
g :: y -> State (A, B) x
h :: y -> State (B, C) x
i :: y -> State (A, C) x

下面是如何在g中调用f

代码语言:javascript
复制
f = do
    -- some stuff
    -- y is bound to an expression somewhere in here
    -- more stuff
    x <- g' y
    -- even more stuff

    where g' y = do
              (a, b, c) <- get
              let (x, (a', b')) = runState (g y) (a, b)
              put (a', b', c)
              return x

这个g'函数是一个丑陋的样板,它除了弥合(A, B, C)(A, B)类型之间的差距之外,什么也不做。它基本上是g的一个版本,它在三元组状态下运行,但保持第三个元组项不变。我正在寻找一种没有样板的情况下编写f的方法。也许是这样的:

代码语言:javascript
复制
f = do
    -- stuff
    x <- convert (0,1,2) (g y)
    -- more stuff

其中,convert (0,1,2)State (a, b) x类型的计算转换为State (a, b, c) x类型。同样,对于所有类型的abcd

  • convert (2,0,1)State (c,a) x转换为State (a,b,c) x
  • convert (0,1)State b x转换为State (a,b) x
  • convert (0,2,1,0)State (c,b) x转换为State (a,b,c,d) x

我的问题:

  1. 有比在元组中放置状态值更好的解决方案吗?我想过要用一个单变压器堆栈。但是,我认为只有对于任何两个函数( fg ),FGGF ( Ff所需的状态值集,Gg所需的状态值集)才能工作。我是不是弄错了?(请注意,我的示例不满足此属性。例如,G = {a, b}H = {b, c}。两者都不是对方的子集。)
  2. 如果没有比元组更好的方法,那么有什么好方法可以避免我提到的样板吗?我甚至愿意用一堆样板函数(见下文)编写一个文件,只要该文件可以自动生成一次,然后就会被遗忘。有更好的办法吗?(我读过关于镜头的文章,但它们的复杂性、丑陋的语法、大量不必要的功能以及对模板Haskell的依赖令人讨厌。这是我的误解吗?镜片能以避免这些问题的方式解决我的问题吗?)

(我提到的函数应该是这样的。)

代码语言:javascript
复制
convert_0_1_2 :: State (a, b) x -> State (a, b, c) x
convert_0_1_2 f = do
    (a, b, c) <- get
    let (x, (a', b')) = runState f (a, b)
    put (a', b', c)
    return x

convert_0_2_1_0 :: State (c, b) x -> State (a, b, c, d) x
convert_0_2_1_0 f = do
    (a, b, c, d) <- get
    let (x, (b', c')) = runState f (b, c)
    put (a, b', c', d)
    return x
EN

回答 2

Stack Overflow用户

发布于 2015-11-26 17:10:03

您可以使用lens-family中的缩放或lens包与tuple-lenses包进行缩放:简化的zoom类型是:

代码语言:javascript
复制
zoom :: Lens' s a -> State a x -> State s x

因此,zoom使用较小的状态运行计算。Lens用于指定较小状态a在较大状态s中的位置。

使用这两个包,您可以运行ghi,如下所示:

代码语言:javascript
复制
f :: State (A,B,C) x
f = do
  zoom _12 g -- _12 :: Lens' (A,B,C) (A,B)
  zoom _23 h -- _23 :: Lens' (A,B,C) (B,C)
  zoom _13 i -- _13 :: Lens' (A,B,C) (A,C)
票数 9
EN

Stack Overflow用户

发布于 2015-11-26 19:59:23

如果你不想在元组上大惊小怪,你可以用一种有记录的“优雅”方法。在lens包中有一些很好的模板Haskell来支持这一点,但是您也可以手工完成。这样做的目的是为状态的每个部分创建至少一个类:

代码语言:javascript
复制
class HasPoints s where
  points :: Lens' s Int

class ReadsPoints s where
  getPoints :: Getter s Int
  default getPoints :: HasPoints s => Getter s Int
  getPoints = points

class SetsPoints s where
  setPoints :: Setter' s Int
  ...

然后,每个操纵状态的函数都有一个类型签名,如下所示

代码语言:javascript
复制
fight :: (HasPoints s, ReadsHealth s) => StateT s Game Player

具有此特殊签名的操作可以完全访问各个点,并可只读访问健康。

票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/33935835

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档