状态设计模式的功能等价

发布于 2024-11-14 18:32:58 字数 141 浏览 2 评论 0原文

与状态设计模式等效的函数式编程是什么?或者更具体地说,状态设计模式的这个维基百科示例将如何转换为 FP?

What would be the functional programming equivalent of the State design pattern? Or more concretely, how would this Wikipedia example of State design pattern will translate to FP?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

惯饮孤独 2024-11-21 18:32:58

此模式是使用 State monad 的示例,这是一种计算
环境可以用状态来增强代码。

这是 Haskell 中的一个实现。

一些帮助器:

import Control.Monad.Trans.State
import Control.Monad.IO.Class
import Data.Char

程序的两种操作模式

data Mode = A | B

使用此模式的状态计算类型,并用计数器进行增强。

type StateM a = StateT (Int, Mode) IO a

write函数,StateM上下文中的函数,
根据有状态模式更改其行为:

writeName :: String -> StateM ()
writeName s = do
    (n,mode) <- get
    case mode of
        A -> do liftIO (putStrLn (map toLower s))
                put (0,B)
        B -> do let n' = n + 1
                liftIO (putStrLn (map toUpper s))
                if n' > 1 then put (n', A)
                          else put (n', B)

运行程序,最初在状态 A 下启动有状态计算

main = flip runStateT (0, A) $ do
    writeName "Monday"
    writeName "Tuesday"
    writeName "Wednesday"
    writeName "Thursday"
    writeName "Saturday"
    writeName "Sunday"

从上面的代码中,main 的输出为:

monday
TUESDAY
WEDNESDAY
thursday
SATURDAY
SUNDAY

请注意,这是一个纯函数式解决方案。该程序中没有可变或破坏性更新。相反,状态单子通过计算线程化所需的模式。

This pattern is an example of the use of the State monad, a computational
environment tha augments code with state.

Here's an implementation in Haskell.

Some helpers:

import Control.Monad.Trans.State
import Control.Monad.IO.Class
import Data.Char

The two modes of operation of the program

data Mode = A | B

The type of stateful computations with this mode, augmented with a counter.

type StateM a = StateT (Int, Mode) IO a

The write function, a function in the StateM context,
changes its behavior based on the stateful mode:

writeName :: String -> StateM ()
writeName s = do
    (n,mode) <- get
    case mode of
        A -> do liftIO (putStrLn (map toLower s))
                put (0,B)
        B -> do let n' = n + 1
                liftIO (putStrLn (map toUpper s))
                if n' > 1 then put (n', A)
                          else put (n', B)

Run the program, launching a stateful computation initially in state A

main = flip runStateT (0, A) $ do
    writeName "Monday"
    writeName "Tuesday"
    writeName "Wednesday"
    writeName "Thursday"
    writeName "Saturday"
    writeName "Sunday"

From the above code, the output of main is:

monday
TUESDAY
WEDNESDAY
thursday
SATURDAY
SUNDAY

Note that this is a purely functional solution. There is no mutable or destructive updates in this program. Instead, the state monad threads the desired mode through the computation.

放肆 2024-11-21 18:32:58

一种编码:

import Data.Char (toUpper, toLower)

newtype State = State { unState :: String -> IO State }

stateA :: State
stateA = State $ \name -> do
    putStrLn (map toLower name)
    return stateB

stateB :: State
stateB = go 2
    where
    go 0 = stateA
    go n = State $ \name -> do
               putStrLn (map toUpper name)
               return $ go (n-1)

不要被 IO 愚弄,这是该模式的纯粹翻译(我们不使用 IORef 来存储状态或任何内容)。展开 newtype,我们可以看到这个类型的含义:

State = String -> IO (String -> IO (String -> IO (String -> ...

它需要一个字符串,执行一些 I/O 并请求另一个字符串,等等。

这是我最喜欢的 OO 中抽象类模式的编码:抽象类->类型、子类 ->该类型的元素。

newtype State 声明取代了抽象 writeName 声明及其签名。我们不传递一个 StateContext 来分配新状态,而是让它返回新状态。将返回值嵌入IO表示允许新状态依赖于I/O。由于在本示例中这在技术上不是必需的,因此我们可以使用更严格的类型

newtype State = State { unState :: String -> (State, IO ()) }

,在该类型中我们仍然可以表达此计算,但状态序列是固定的并且不允许依赖于输入。但让我们坚持原来的、更宽松的类型。

对于“测试客户端”:

runState :: State -> [String] -> IO ()
runState s [] = return ()
runState s (x:xs) = do
    s' <- unState s x
    runState s' xs

testClientState :: IO ()
testClientState = runState stateA
                   [ "Monday"
                   , "Tuesday"
                   , "Wednesday"
                   , "Thursday"
                   , "Saturday"
                   , "Sunday" ]

One encoding:

import Data.Char (toUpper, toLower)

newtype State = State { unState :: String -> IO State }

stateA :: State
stateA = State $ \name -> do
    putStrLn (map toLower name)
    return stateB

stateB :: State
stateB = go 2
    where
    go 0 = stateA
    go n = State $ \name -> do
               putStrLn (map toUpper name)
               return $ go (n-1)

Don't be fooled by the IO, this is a pure translation of that pattern (we are not using an IORef to store the state or anything). Expanding the newtype, we see what this type means:

State = String -> IO (String -> IO (String -> IO (String -> ...

It takes a string, does some I/O and asks for another string, etc.

This is my favorite encoding of abstract class patterns in OO: abstract class -> type, subclasses -> elements of that type.

The newtype State declaration takes the place of the abstract writeName declaration and its signature. Instead of passing a StateContext into which we assign a new state, we just have it return the new state. Embedding the return value in IO says that the new state is allowed to depend on I/O. Since that is not technically necessary in this example, we could use the more stringent type

newtype State = State { unState :: String -> (State, IO ()) }

in which we can still express this computation, but the sequence of states is fixed and not allowed to depend on input. But let's stick to the original, more lenient type.

And for the "test client":

runState :: State -> [String] -> IO ()
runState s [] = return ()
runState s (x:xs) = do
    s' <- unState s x
    runState s' xs

testClientState :: IO ()
testClientState = runState stateA
                   [ "Monday"
                   , "Tuesday"
                   , "Wednesday"
                   , "Thursday"
                   , "Saturday"
                   , "Sunday" ]
作死小能手 2024-11-21 18:32:58

也许使用 State monad 与自定义修饰符和访问器相结合?

Maybe with a State monad combined with custom modifiers and accessors?

匿名的好友 2024-11-21 18:32:58

我不认为状态模式有纯功能上的等价物。因为纯函数式编程没有状态和时间的概念。状态模式本质上是关于状态和时间的。但我认为非纯函数等价物存在,它是无限惰性评估流。您可以使用 C# Yield 来实现它。

I don't think there's pure functional equivalent for state pattern. Because pure functional programming doesn't have the concept of state and time. State pattern is intrinsically about state and time. But I think the non-pure functional equivalent exists, it's infinite lazy evaluated stream. You can implement it with C# yield.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文