当布尔/标记联合确定递归函数的当前执行是否在第一次迭代时,术语是什么?

时间:2015-05-04 03:35:34

标签: haskell recursion erlang

我记得在Erlang中看到,递归函数的包装函数有时会传递一个原子,该原子确定递归是在第一次迭代(n = 1)还是一些连续迭代(n> 1)。当递归函数在第一次迭代后需要更改其行为时,这很有用。这种模式叫什么?

此外,这种模式在Haskell中也适用吗?我用它编写了一个小片段,查看first布尔值:

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在此提供了更广泛的基于类型的函数匹配工具)。换句话说,某种风格的“有限状态机”就是你要找的东西。

     

状态可以设置为函数名称或状态参数;使用哪个取决于上下文和语言,这可以扩展到作为函数名称的state参数,并且它本身就是一种匹配。

     

Haskell的最佳选择通常不是最适合Erlang的。许多一次性任务被委托给Erlang中的单独进程,甚至Erlang中的进程实例化在调用init时也会经历一个“初始状态”,这与当你说“递归函数需要更改它时的情况基本相同”第一次迭代后的行为“。 OTOH,Haskell提供了更多匹配函数头的方法。在任何一种情况下,采用命名函数定义操作状态的方法更清晰。结果将是不嵌套的代码,不需要程序条件,并且可以更容易地从任何地方调用(稍后当您重新编写程序时更灵活地处理......)。

     

FSM是确定基于状态执行什么代码的一般方法,并且函数(或进程)的初始化是一种特殊情况。我听说这个称为“传递初始化”,如入口函数定义接口,进行一次性处理以设置主程序并将执行传递给它:

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 个答案:

答案 0 :(得分:4)

要扩展我对boolean blindness的评论,我不仅仅意味着使用其他类型同构2,而是使用正确的类型来编码原因你的递归函数关心它是哪个迭代。

将您的代码与以下版本进行比较,我会说更干净,更有吸引力。它取决于将Maybe Integer而不是(Integer, Bool)传递给accumulateNumber

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

答案 1 :(得分:0)

还想指出我发现了一篇讨论这种确切技术的学术论文。

它被Andy Gill&amp; amp;所谓的“工人/包装变革”称为Graham Hutton(2009)

链接:http://www.cs.nott.ac.uk/~gmh/wrapper.pdf