Raku 中函数对象“power”运算符的实现 测试奖金乐/APL信息:TL;DR重复组合身份优先级签名

如何解决Raku 中函数对象“power”运算符的实现 测试奖金乐/APL信息:TL;DR重复组合身份优先级签名

在 APL 中有幂运算符 ,如果将其应用于函数 f,则会叠加 f 的应用。如何在 Raku 中实现该运算符?

例如,对于 f 的定义:

sub f(Int:D $i){ $i + 1 }

命令 say (f ⍣ 4)(10); 应该等同于 say f(f(f(f(10))));

我下面的实现是针对一个带一个参数的函数。

问题

  1. 应该如何使用适用于多个(或任何)签名的更好的实现来扩展或替换它?

  2. 如何定义新的幂运算符的“高优先级”?

  3. 是否有更好的方法来定义 f ⍣ 0 的“身份函数”结果?

参考链接

这里是对 APL 的 的描述: "Power Operator"

是一个“带两个点的星星”,或者更正式的"Apl Functional Symbol Star Diaeresis"。)

尝试

这是一个实现的尝试:

sub infix:<⍣>( &func,Int:D $times where $times >= 0 ) {
    if $times == 0  {
      sub func2($d) {$d}
    } else {
      sub func2($d) {
        my $res = &func($d);
        for 2..$times -> $t { $res = &func($res) }
        $res
      }
    }
}

sub plus1(Int:D $i){ $i + 1 }

say 'Using (&plus1 ⍣ 0) over 4 :';
say (&plus1 ⍣ 0)(4);

say 'Using (&plus1 ⍣ 10) over 4 :';
say (&plus1 ⍣ 10)(4);

# Using (&plus1 ⍣ 0) over 4 :
# 4
# Using (&plus1 ⍣ 10) over 4 :
# 14

(我遵循了这个教程页面:https://docs.raku.org/language/optut。)

测试

由提供的定义 Brad Gilbert's answer 通过以下所有测试。

use Test;

sub infix:<⍣> ( &func,UInt $repeat ) is tighter( &[∘] ) { [∘] &func xx $repeat }

proto plus1(|) {*}
multi plus1(Int:D $i){ $i + 1 }
multi plus1(Bool:D $b){ $b.Int + 1 }
multi plus1(Int:D $i,Bool:D $b){ $i + $b.Int + 1 }
multi plus1(Int:D $i,Int:D $j){ $i + $j + 1 }
multi plus1(@j){ ([+] @j) + 1}
multi plus1(Int:D $i,@j){ plus1($i) + plus1(@j) - 1 }
multi plus1(%h){ ([+] %h.values) + 1 }

plan 9;

is plus1([1,3,5,3]),13,'plus1([1,3])';

is plus1(3,[1,16,'plus1(3,True),True)';

is (&plus1 ⍣ 0)(4),4,'(&plus1 ⍣ 0)(4)';

is (&plus1 ⍣ 10)(4),14,'(&plus1 ⍣ 10)(4)';

is (&plus1 ⍣ 10)([1,22,'(&plus1 ⍣ 10)([1,3])';

is (&plus1 ⍣ 3)(4,8,'(&plus1 ⍣ 3)(4,True)';

is (&plus1 ⍣ 3)(3,18,'(&plus1 ⍣ 3)(3,3])';

is (&plus1 ⍣ 3)({a => 1,b => 3,c => 5}),12,'(&plus1 ⍣ 3)({a => 1,c => 5})';

done-testing;

解决方法

  1. 应该如何扩展或用更好的实现替换它 适用于多个(或任何)签名?

Raku 实际上有一个与 APL 内置的 相距不远的运算符:xx,列表重复运算符。 (毕竟,函数列表仍然是列表!)。使用 xx,我们可以在一行中定义

sub infix:<⍣>(&fn,$count) { &{($^ω,|(&fn xx $count)).reduce({$^b($^a)})} };

然后像在您的 &plus1 示例中一样使用它:

sub plus1(Int:D $i){ $i + 1 }

say 'Using (&plus1 ⍣ 0) over 4 :';
say (&plus1 ⍣ 0)(4);

say 'Using (&plus1 ⍣ 10) over 4 :';
say (&plus1 ⍣ 10)(4);

# Using (&plus1 ⍣ 0) over 4 :
# 4
# Using (&plus1 ⍣ 10) over 4 :
# 14

如果您想要更详细(支持一元函数和二元函数,并接受一个函数作为您提到的第二个操作数 way APL does),那么您可以将其扩展为这样的:


#| Monadic repetition
multi infix:<⍣>(&fn:($),$count) { 
    &{($^ω,|(&fn xx $count)).reduce({$^b($^a)})} };
#| Dyadic repetition
multi infix:<⍣>(&fn,$i) { 
    sub (|c){(|c[0],|(&fn xx $i)).reduce: -> $sum,&f { f $sum,|c[*-1]}}};

#| "until" function operand
multi infix:<⍣>(&fn1,&fn2) {
    sub ($a,$b) { given fn1($a,$b) { when .&fn2($a) { $a }
                                       default        { .&?ROUTINE($b) }}}}

sub plus1(Int:D $i,Bool:D $b) { $i + $b.Int + 1 }
say (&plus1 ⍣ 6)(1,True);                  # OUTPUT: «13»

say (&{$^b + 1 ÷ $^a} ⍣ &infix:<≅>)(1,1);  # OUTPUT: «1.618033989»
# equivalent to  1+∘÷⍣=1 ⍝ fixpoint: golden mean.

(请注意,您可以扩展第二个 multi 以采用元数大于 2 的函数,但您需要决定在这些情况下您想要什么语义。还要注意 Raku 有一个 &infix:<∘> 运算符,但是它与 APL 的作用不同,因为它不传递调用参数。)

唯一没有让我们从 Dyalog 的幂运算符中获得的是能够提供一个负整数操作数来应用函数操作数的倒数。我们可以编写那个 multi,但是 Raku(就像几乎所有非 APL 语言一样)几乎没有系统地创建反函数,所以它似乎不太值得。

  1. 如何定义新的权力运营商的“高优先级”?

您可以使用 is tighter/is equiv/is looser precedence traits 指定优先级。我不确定您想要的优先级到底有多高,但您基本上可以随心所欲地获得它。

  1. 是否有更好的方法来定义 f ⍣ 0 的“恒等函数”结果?

通过将 定义为减少,这自然会发生。

奖金乐/APL信息:

这不是回答您的问题所必需的,但如果您对 Raku 和 APL 都感兴趣,这似乎是您可能想知道的:所有 Raku 函数都可以在 infix form 中调用,这使得它们非常类似于 APL 二元函数。所以我们可以这样写:

my &f = &plus1 ⍣ 6;
say 1 [&f] True;

效果与say (&plus1 ⍣ 6)(1,True);相同。

此外,如果我理解正确的话,APL 幂运算符最常使用的方法之一是通过使用布尔操作数的“条件函数应用程序”。 Raku 支持与 &infix:<xx> 列表重复运算符非常相似的习语。所以,你可能在 APL 中有这个:

SquareIfNegative ← { (*∘2⍣(⍵<0)) ⍵ }

在乐乐,那可能是

my &square-if-negative = &{ $_² xx ($_ < 0)};
,

TL;DR

(太长;没读)
这非常有效。

sub infix:<⍣> ( &func,UInt $repeat ) { [∘] &func xx $repeat }

重复组合

在数学中,乘法 × 很像重复加法 +

2 × 5   =   2 + 2 + 2 + 2 + 2

还有其他方法可以在 Raku 中编写。

[+] 2,2,2
[+] 2 xx 5

Raku 也有一个函数组合器,很像函数加法
(ASCII 版本只是 o。)

sub A (|) {…}
sub B (|) {…}
my \var = …;

A( B( var ))   eqv   (&A ∘ &B).(var)

首先看看我们如何在您的代码中使用该运算符。

sub plus1(Int:D $i){ $i + 1 }

my &combined = &plus1 ∘ &plus1;

say combined 0; # 2

就像我们可以将乘法定义为实际上是重复加法一样。

{
  sub infix:<×> ( $val,$repeat ) {
    &CORE:infix:<×>(
      [+]( $val xx $repeat.abs ),# <--
      $repeat.sign
    )
  }

  say 2 × 5;
}

我们可以为您的运算符做同样的事情,根据重复的函数组合来定义它。

sub infix:<⍣> ( &func,UInt $repeat ) {
  [∘] &func xx $repeat
}

sub plus1(Int:D $i){ $i + 1 }

say (&plus1 ⍣  1)(0); # 1
say (&plus1 ⍣ 10)(0); # 10

身份

我没有专门处理 $repeat0 的情况。
尽管如此,它仍然做了一些明智的事情。

say (&plus1 ⍣ 0)(5); # 5

那是因为 [∘]() 的无参数形式只返回一个函数,该函数只返回其输入不变。

say (&plus1 ⍣ 0)('foobar'); # foobar

say [∘]().('foobar'); # foobar

基本上,身份结果已经按照您的意愿行事。


您可能想知道当参数为零时 [∘] … 如何知道返回什么。

say ( [∘]      ).(4); # 4

事实并非如此。
或者更确切地说,[ ] … 没有,但 &infix:<∘> 有。

.say for &infix:<∘>.candidates».signature;
# ()
# (&f)
# (&f,&g --> Block:D)

这两者都返回合理的原因是相同的。

say [+]  ; # 0
say [×]  ; # 1

优先级

设置优先级的最佳方法是首先找出其他运算符具有类似的优先级。

我将以 为例来定义它。

  • 您可以将其设置为与 is equiv 相同的优先级。 (还将结合性设置为相同。)

      sub sub infix:<⍣> ( &func,UInt $repeat ) is equiv( &infix:<∘> ) {…}
    

    请注意,由于 Raku 有很多地方指的是现有的中缀运算符,因此引用它们的方法更短。

      sub sub infix:<⍣> ( &func,UInt $repeat ) is equiv( &[∘] ) {…}
      #                                                   ^--^
    
  • 您可以使用 is looser 将其设置为具有更宽松的优先级。

      sub sub infix:<⍣> ( &func,UInt $repeat ) is looser( &[∘] ) {…}
    
  • 或者您可以使用 is tighter 将其设置为更严格的优先级。
    (在这种情况下更有意义,因为 ×tighter 而不是 +,所以 也应该比 更紧。)

      sub sub infix:<⍣> ( &func,UInt $repeat ) is tighter( &[∘] ) {…}
    

默认优先级与 + 运算符相同。


签名

至于签名, 只是将每个结果传递给下一个。

sub A ( \a ) {
  a + 1
}
sub B ( \a,\b ) {
  a + b
}

say A(B(4,5)); # 10

say (&A ∘ &B).(4,5); # 10

假设上面的 A 期望两个值,而 B 提供两个值作为列表。

sub A ( \a,\b ) {
  a + b
}
sub B ( \a,\b ) {
  a + b,1
}

那么这条线就失败了。
它实际上甚至无法编译。

say A(B(4,5));

这一行没有失败,实际上它返回了正确的值

say (&A ∘ &B).(4,5); # 10

如果你给它多个字幕,它也会正常工作

所以总的来说,仅使用 [∘]xx 效果非常好。

sub infix:<⍣> ( &func,UInt $repeat ) {
  [∘] &func xx $repeat
}

say (&plus1 ⍣ 3)(4);

实际上,您的操作员不会使此类代码比我们用来实现您的操作员的代码短得多。 (仅缩短了 4 个字符。)

say [∘](&plus1 xx 3)(4);

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 &lt;select id=&quot;xxx&quot;&gt; SELECT di.id, di.name, di.work_type, di.updated... &lt;where&gt; &lt;if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 &lt;property name=&quot;dynamic.classpath&quot; value=&quot;tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-