R中的Lambda.r函数式编程

2020-12-30 02:10:45

提供用于在R中编写功能程序的语法.Lambda.r具有cleansyntax,用于使用可选的保护语句定义多部分功能。还支持简单模式匹配。可以使用相同的功能符号轻松定义和实例化类型。类型检查是集成的和可选的,从而使程序员在其应用程序或程序包上具有完全的灵活性。

使用%as%表示法定义功能。函数定义中可以包含任何代码块。

多部分函数定义很容易构造。对于简单标准,可以在lambda.r中直接使用文字的模式匹配。

与简单模式匹配相比,在多部分函数中执行不同的函数变体有时需要更多的细节。对于这些情况,使用保护声明来定义执行条件。保护只是函数定义中的一个附加子句。

仅当所有保护声明都为true时,函数变体才执行。可以在块中添加所需数量的保护声明。只需用换行符或分号将它们分开。

注意,在上面的示例中,可以使用类型声明来处理类型检查,这将在下面进行讨论。

对于由多个部分定义的功能,每个单独的功能变体的定义顺序与定义顺序相同。因此,在函数定义链的早期定义为true的限制性较小的变量将优先于稍后定义的变量。 (如果按照顺序进行操作,则将不会调用fib(0)和fib(1)的模式匹配,因为第一个定义fib(n)始终会变为true。

Lambda.R引入类型作为类的替代。类型是附加了类型信息的数据结构。像类一样,构造函数也存在于类型中,并且一种类型可以从另一种类型继承。区别在于类型没有嵌入式方法。在函数式编程中,函数是一流的,因此无需将其嵌入数据结构中。使用类型可提供类型安全性,这意味着仅当类型正确时才执行函数变量。

通过定义其构造函数来定义类型。我们使用大写的函数名称定义构造函数。构造函数的返回值是自动键入的。因此,值x将为Integer类型。

实例化类型就像调用函数一样简单。使用标准的S3自省功能类检查类型。 %isa%运算符还可用于测试对象是否为特定类型。

类型约束可以添加到函数中。除了返回类型外,这些约束还指定每个输入参数的类型。使用这种方法可确保在调用函数时,参数只能具有兼容的类型。约束中的最终类型是返回类型,返回类型在调用函数后进行检查。如果结果的返回类型不正确,则调用将失败。

fib(n)%::%整数:Integerfib(0)%as%Integer(1)fib(1)%as%Integer(1)fib(n)%as%{fib(n-1)+ fib(n -2)} fib(x)

通过调用fib(Integer(1))正确键入参数将提供正确的输出。请注意,模式匹配甚至适用于自定义类型。

类型约束必须在函数实现之前声明。声明之后,类型声明将保留作用域,直到另一个声明了相同数量参数的类型声明为止(有关示例,请参见tests / types.R)。

就像lambda.r中定义的自定义类型一样,有很多受支持的内置类型。这些类型使用相同的语法。在上面的示例中,我们可以轻松地声明

注意:对于类型为function的对象,由于解析器的优先规则,您不能指定' function'。在类型约束中。而是使用' Function'。

类型约束是有用的,但是约束过于具体会破坏函数的多态性。要在保留某种类型安全性的同时保留它,可以使用类型变量。对于类型变量,不检查实际类型。而是要检查类型之间的关系。

fib(n)%::%a:afib(0)%as%1fib(1)%as%1fib(n)%as%{fib(n-1)+ fib(n-2)}

请注意,对于类型变量唯一有效的字符是小写字母(即a-z)。如果对于单个函数定义,您需要的还不止此,那么您还会遇到其他问题。

可以将省略号插入类型约束中。它具有有趣的属性,因为省略号表示一组参数。若要指定输入值应由省略号捕获,请在类型约束内使用...。例如,假设您需要一个将一组数字之和相乘的函数。省略号类型告诉slambda.r绑定与省略号类型关联的类型。

sumprod(x,...,na.rm = TRUE)%::%数值:...:逻辑:numericsumprod(x,...,na.rm = TRUE)%as%{x * sum(.. 。,na.rm = na.rm)}> sumprod(4,1,2,3,4)[1] 40

另外,假设您希望绑定到省略号的所有值均为某种类型。然后,您可以将...附加到具体类型。

sumprod(x,...,na.rm = TRUE)%::%数值:数字...:逻辑:numericsumprod(x,...,na.rm = TRUE)%as%{x * sum(。 ..,na.rm = na.rm)}> sumprod(4,1,2,3,4)[1] 40> sumprod(4,1,2,3,4,' a')UseFunction(sumprod," sumprod&#34 ;, ...)中的错误:&#39没有有效功能; sumprod(4,1,2,3,4,a)'

如果要保留多态性,但仍将与省略号绑定的值限制为单个类型,则可以使用类型变量。请注意,适用于类型变量的相同规则。因此,类型变量表示在其他地方未指定的类型。

sumprod(x,...,na.rm = TRUE)%::%a:a ...:逻辑:asumprod(x,...,na.rm = TRUE)%as%{x * sum(。 ..,na.rm = na.rm)}> sumprod(4,1,2,3,4)[1] 40> sumprod(4,1,2,3,4,' a')UseFunction(sumprod," sumprod&#34 ;, ...)中的错误:&#39没有有效功能; sumprod(4,1,2,3,4,a)'

有时,忽略约束中的特定类型很有用。由于我们无法推断程序中的所有类型,因此这是可以接受的操作。使用 。类型约束中的值告诉lambda.r不检查给定参数的类型。

例如在R f(x,y)%::%中。 :numeric:数字,将不检查x的类型。

fib(n)%::%数字:numericfib(0)%as%1fib(1)%as%1fib(n)%as%{fib(n-1)+ fib(n-2)} fib(5)密封件

要完全忽略类型,只需忽略上面清单中的类型声明,代码将对其进行评估。

Integer(x)%as%x fib(n)%::%Integer:Integerfib(0)%as%Integer(1)fib(1)%as%Integer(1)fib(n)%as%{fib( n-1)+ fib(n-2)} x<-整数(5)fib(x)

第一个示例中的seal命令可防止将新语句添加到现有函数定义中。相反,新定义将重置功能。通常,您不需要此函数,因为lambda.r会自动替换具有相同签名的函数定义。

lambda.r仍支持R函数调用的所有重要功能。另外,lambda.r提供了一些解析转换以添加一些额外的功能,从而使应用程序开发更快。

属性是装饰对象的一种元数据形式。该信息可用于简化保留多态性并与现有功能兼容的类型结构,同时提供应用程序所需的详细信息。Lambda.R提供了方便的语法,可通过@symbol与属性进行交互。

冻结(x)%::%温度:逻辑冻结(x)%%{x @ system ==' metric' x @单位=='摄氏'}%as%{如果(x< 0){是}其他{假}}

请注意,在lambda.r之外,您必须使用标准attr()函数来访问特定属性。另请注意,尚未使用S4对象测试属性。

R中一个很好的便利是可以使用默认值指定可选参数。 Lambda.R在多部分函数定义中保留了此功能。函数根据定义的顺序进行匹配,对于带有可选参数的函数也是如此。

温度(x,系统=& 34;单位=&℃;')%,以%{x @系统<-系统x @单位<-单位x}> ctemp<-温度(20)> ctemp [1] 20attr(," system")[1]" metric" attr(," units")[1]" " attr(," class")[1]"温度" "数字"

lambda.r内置了对省略号参数的支持。必需的参数必须仍然匹配,而任何其他参数将包含在省略号中。这是一个使用lm帮助页面中包含的工厂数据的示例。

回归(公式,...,na.action =' na.fail')%as%{lm(公式,...,na.action = na.action)} ctl<-c (4.17,5.58,5.18,6.11,4.50,4.61,5.17,4.53,5.33,5.14)trt<-c(4.81,4.17,4.41,3.59,5.87,3.83,6.03,4.89,4.32,4.69)数据< -data.frame(group = gl(2,10,20,标签= c(" Ctl&#34 ;," Trt")),权重= c(ctl,trt)) lm.D9<-回归(权重〜组,数据=数据)

确实需要小心使用省略号,因为它的行为就像贪婪的匹配一样,因此在函数变体中使用省略号时,后续定义可能无法按预期工作。

最重要的示例暗示了支持命名参数。命名参数可以与位置参数混合并匹配,就像在传统函数定义中一样。

从1.1.0版开始,lambda.r可以检测到重复的函数签名并更新现有定义。这意味着开发效率更高,因为您可以重新获得文件资源,并且现有定义将根据您的期望进行更新。此过程与多部分函数定义和类型约束兼容。请注意,在使用类型约束时,只能自动替换与活动类型约束关联的功能。原因是可能存在两个相同的函数签名,而lambda.r确实无法知道您的意思。因此,您必须通过类型约束告诉lambda.r。

例如,采用此简单的倒数功能。有两个类型约束子句和三个总函数变体。变体2和3的签名是相同的,因此区别它们的唯一一件事就是与每个变量相关联的不同类型约束。请注意,变体2的定义中存在一个明显的错误。

倒数(n)%::%数值:数倒数(0)%as停止("倒数0未定义")倒数(n)%as%{2 / n}倒数(n)% ::%字符:numericreciprocal(n)%as%{reciprocal(as.numeric(n))}

要更改变体2的定义,必须重新声明第一个类型约束。否则,lambda.r将不知道是否更新变量2或3。

Lambda.R无法知道一个函数定义是否完整。明确告诉lambda.r将确保任何新的函数定义都将重置该函数,而不是附加另一个定义。

如果提供广泛的接口,请注意不要密封该功能。密封类似于在Java中将变量定为final,因此无法进行进一步的修改。关键区别在于,尝试向密封函数添加更多定义将覆盖现有定义。此行为旨在使应用程序和程序包开发更加迭代。

lambda.r中的一个函数具有许多附加的元数据。访问原始数据可能会很麻烦,因此lambda.r提供了提取有用位的工具。查看功能的基本信息是通过在shell中对功能进行类型划分来完成的。这会导致函数的类型声明和函数签名的转储。

> fib<函数> [[1]] fib(n)%::%整数:整数fib(0)%as%... [[[2]] fib(n)%::%整数:Integer fib(1)% as%... [[3]] fib(n)%::%整数:整数fib(n)%as%...

要查看完整的功能定义,请使用'功能以获取特定功能变体的定义。分隔每个变量的数字是要使用的索引。

> describe(fib,3)function(n){整数(fib(n-1)+ fib(n-2))}<环境:0x10488ed10>

类型构造器类似于普通函数,并且相同的技术也可以用来查看类型主体。

如果您想在R中玩单子游戏,那么lambda.r可以助您一臂之力,以下是一些示例。请注意,lambda.tools实现了一些常用的运算符。

可能(a)%:=%aJust(a)%:=%可能(a)Nothing()%:=%可能(NA)mreturn(x)%as%Just(x)m%> =% g%when%{is.null(m)}%as%NULLm%> =%g %% ::%Just:Function:Maybem%> ==%g%as%g(m)m% > =%g%::%否:功能:Maybem%> ==%g%as%毫米%> =%g%::%Just:功能:Maybem%> =%g%as%g(m)

f%> =>%g%:=%{function(x)f(x)%> ==%g} f%< =<%g%:=%{function(x)g (x)%>> =%f} safelog(x)%::%数字:也许当%{x< = 0}%as%Nothing()safelog(x)%:=%时,safelog(x) Just(log(x))safesqrt(x)%::%数字:Maybesafesqrt(x)%w%{x< = 0}%as%Nothing()safesqrt(x)%:=%Just(sqrt(x) ))safelogsqrt<-safelog%&=; =<%safesqrt

标准调试功能不能与lambda.r函数一起使用。而是使用包含的函数debug.lr和undebug.lr。这些功能使您可以通过完整的多部分功能调用进行调试。

如果您尝试破坏lambda.r,则很可能会成功。 有些事情行不通,但是大多数用例都可以正常工作。 让我知道是否找到了失败的事物,但不要仅仅为了打破它就破坏它。 以下是一些行不通的事情。 当形式为{{sum(a,b,c)== 1}%as%{1}时,形式为f(a,b,c)的后卫 形式为f(x)的支援卫兵,当%{length(grep(' foo',x))> 0}%as%{1} 默认情况下锁定功能(检查下一个功能的名称是否不同)。 Usepublic()作为表示功能的方法可以全局修改