Haskell 什么';当布尔/标记的并集确定递归函数的当前执行是否在其第一次迭代时的术语是什么?
我记得在Erlang中看到,递归函数的包装器函数有时会传递一个原子,以确定递归是在第一次迭代(n=1)还是在某些连续迭代(n>1)时进行。当递归函数需要在第一次迭代后更改其行为时,这非常有用。这种模式叫什么 此外,这种模式是否也适用于Haskell?我用它写了一个小片段,首先看Haskell 什么';当布尔/标记的并集确定递归函数的当前执行是否在其第一次迭代时的术语是什么?,haskell,recursion,erlang,Haskell,Recursion,Erlang,我记得在Erlang中看到,递归函数的包装器函数有时会传递一个原子,以确定递归是在第一次迭代(n=1)还是在某些连续迭代(n>1)时进行。当递归函数需要在第一次迭代后更改其行为时,这非常有用。这种模式叫什么 此外,这种模式是否也适用于Haskell?我用它写了一个小片段,首先看布尔值: import Data.Char (digitToInt, isDigit) data Token = Num Integer deriving (Show) tokeniseNumber :: String
布尔值:
import Data.Char (digitToInt, isDigit)
data Token = Num Integer deriving (Show)
tokeniseNumber :: String -> (String, Maybe Token)
tokeniseNumber input = accumulateNumber input 0 True
where
accumulateNumber :: String -> Integer -> Bool -> (String, Maybe Token)
accumulateNumber [] value True = ([], Nothing)
accumulateNumber [] value False = ([], Just (Num value))
accumulateNumber input@(peek:tail) value first =
case isDigit peek of
False | first -> (input, Nothing)
False | not first -> (input, Just (Num value))
True -> accumulateNumber tail (value * 10 + (toInteger . digitToInt) peek) False
--编辑--
zxq9发布了一个答案,后来被删除。但我实际上认为答案是有价值的
将其定义为一组单独的函数,每个函数都有特定的行为方式,以及一个函数头匹配,确定要分派哪些函数(Haskell在这里提供了更广泛的基于类型的函数匹配工具)。换句话说,您正在寻找某种类型的“有限状态机”
可以将状态设置为函数名或状态参数;使用哪一个取决于上下文和语言,这可以扩展到状态参数,它是一个函数名,而它本身是一种匹配
对Haskell最好的通常不是对Erlang最好的。许多一次性任务被委托给Erlang中的单独进程,甚至Erlang中的进程实例化在调用init时也会经历“init状态”,这与您所说的“递归函数在第一次迭代后需要更改其行为时”基本相同。OTOH,Haskell提供了更多的方法来匹配功能头。在任何一种情况下,采用命名函数定义操作状态的方法都更为简洁。结果将是不嵌套的代码,不需要过程条件,并且可以更容易地从任何地方调用(以后重新编写程序时处理更灵活…)
FSM是一种基于状态确定要执行哪些代码的通用方法,函数(或进程)的初始化就是这种情况的特例。我听说过这叫做“传递初始化”,如中所述,entry函数定义接口,进行一次性处理以设置主过程,并将执行传递给它:
init(Args) ->
{ok, NewArgs} = one_time_processing(Args),
loop(NewArgs).
loop(Args) ->
{ok, NewArgs} = do_stuff(Args),
loop(NewArgs).
当然,上面是一个无限循环,因此更常见的是在循环/1函数的末尾检查退出,或者(更常见的是)在循环的函数头中进行匹配:
loop(done, Args) ->
Args;
loop(cont, Args) ->
{Cond, NewArgs} = do_stuff(Args),
loop(Cond, NewArgs).
但在任何一种情况下,都最好让流程的初始化是它自己的过程,与循环体分开定义。其他具有循环结构的语言处理这一问题的方式不同,根据程序员选择的循环样式,以特殊方式应用一些条件检查组合,但效果是相同的。通常,实现这一过程最明显的方法是执行相同的操作:将整个循环包装在函数调用之后,循环之前的步骤是“一次性”初始化部分。在这种情况下,不是循环被“包装”在函数调用中,而是您编写了一个接口函数来访问它,它在调用它的过程中进行了一些一次性初始化
为了扩展我的评论,我不仅仅是指使用与
2
同构的另一种类型,而是要使用正确的类型来编码递归函数关心哪个迭代的原因
将您的代码与下面的版本进行比较,我认为它更干净、更简洁。它取决于将一个可能是整数
而不是(整数,Bool)
传递给累加器枚举器
import Data.Char (digitToInt, isDigit)
import Data.Maybe
import Control.Applicative
data Token = Num Integer deriving (Show)
tokeniseNumber :: String -> (String, Maybe Token)
tokeniseNumber input = accumulateNumber input Nothing
where
accumulateNumber :: String -> Maybe Integer -> (String, Maybe Token)
accumulateNumber input@(peek:tail) value
| isDigit peek = accumulateNumber tail (Just $ toNum (fromMaybe 0 value) peek)
accumulateNumber input value = (input, Num <$> value)
toNum value peek = value * 10 + (toInteger . digitToInt) peek
import Data.Char(DigitPoint,isDigit)
导入数据,也许吧
导入控制
数据标记=Num整数派生(显示)
令牌编号::字符串->(字符串,可能是令牌)
tokeniseNumber输入=累加器枚举器无输入
哪里
累积枚举器::字符串->可能是整数->(字符串,可能是令牌)
累计枚举器输入@(peek:tail)值
|isDigit peek=AccumerateNumber tail(仅$toNum(可能为0值)peek)
累计枚举器输入值=(输入,Num值)
音调值峰值=值*10+(toInteger.DigitPoint)峰值
为了扩展我的评论,我不仅仅是指使用与2
同构的另一种类型,而是使用正确的类型来编码递归函数关心哪个迭代的原因
将您的代码与下面的版本进行比较,我认为它更干净、更简洁。它取决于将一个可能是整数
而不是(整数,Bool)
传递给累加器枚举器
import Data.Char (digitToInt, isDigit)
import Data.Maybe
import Control.Applicative
data Token = Num Integer deriving (Show)
tokeniseNumber :: String -> (String, Maybe Token)
tokeniseNumber input = accumulateNumber input Nothing
where
accumulateNumber :: String -> Maybe Integer -> (String, Maybe Token)
accumulateNumber input@(peek:tail) value
| isDigit peek = accumulateNumber tail (Just $ toNum (fromMaybe 0 value) peek)
accumulateNumber input value = (input, Num <$> value)
toNum value peek = value * 10 + (toInteger . digitToInt) peek
import Data.Char(DigitPoint,isDigit)
导入数据,也许吧
导入控制
数据标记=Num整数派生(显示)
令牌编号::字符串->(字符串,可能是令牌)
tokeniseNumber输入=累加器枚举器无输入
哪里
累积枚举器::字符串->可能是整数->(字符串,可能是令牌)
累计枚举器输入@(peek:tail)值
|isDigit peek=AccumerateNumber tail(仅$toNum(可能为0值)peek)
累计枚举器输入值=(输入,Num值)
音调值峰值=值*10+(toInteger.DigitPoint)峰值
还想指出,我发现了一篇学术论文,讨论了这项精确的技术
Andy Gill&Graham Hutton(2009)称之为“工人/包装器转换”
链接:我还想指出,我发现了一篇学术论文,讨论了这项精确的技术 Andy Gill&Graham Hutton(2009)称之为“工人/包装器转换” 链接:当心;我认为在大多数特定情况下,您可以使用比单个布尔值更好的东西(但它会因用例而异)