从 Raku 和 C 函数返回的数组在 nativecall 中不匹配

Sum*_*nal 5 c rakudo nativecall raku

我想从 Raku 调用 C 函数https://github.com/wch/r-source/blob/trunk/src/nmath/rmultinom.c#L47。\n为此,我将 Raku 函数定义为

\n
use NativeCall;\nconstant RMATH = "./Rmath.dll";\nmulti set_seed(uint32, uint32)\n    is native( RMATH ) { * };\nmulti set_seed(UInt() $a, UInt() $b) is export {\n    set_seed(my uint32 $ = $a, my uint32 $ = $b)\n}\nsub rmultinom(int32, CArray[num64], int32, CArray[int32])\n    is native( RMATH ) { * }; \n\nsub raku_rmultinom($n , $size, @prob is copy) is export {\n    @prob = @prob.map: {$_.Num};\n    my $prob = CArray[num64].new(@prob);\n    my $ints = CArray[int32].allocate($size);\n    rmultinom($n, $prob, $size, $ints);\n    return $ints.list\n}\n
Run Code Online (Sandbox Code Playgroud)\n

一个电话

\n
my @prob = [0.1, 0.3, 0.5, 0.1];\nset_seed(123,456);\nsay raku_rmultinom(100, 5, @prob);\n
Run Code Online (Sandbox Code Playgroud)\n

给出

\n
(9 29 49 13 0)\n
Run Code Online (Sandbox Code Playgroud)\n

符合C代码:

\n
use NativeCall;\nconstant RMATH = "./Rmath.dll";\nmulti set_seed(uint32, uint32)\n    is native( RMATH ) { * };\nmulti set_seed(UInt() $a, UInt() $b) is export {\n    set_seed(my uint32 $ = $a, my uint32 $ = $b)\n}\nsub rmultinom(int32, CArray[num64], int32, CArray[int32])\n    is native( RMATH ) { * }; \n\nsub raku_rmultinom($n , $size, @prob is copy) is export {\n    @prob = @prob.map: {$_.Num};\n    my $prob = CArray[num64].new(@prob);\n    my $ints = CArray[int32].allocate($size);\n    rmultinom($n, $prob, $size, $ints);\n    return $ints.list\n}\n
Run Code Online (Sandbox Code Playgroud)\n
my @prob = [0.1, 0.3, 0.5, 0.1];\nset_seed(123,456);\nsay raku_rmultinom(100, 5, @prob);\n
Run Code Online (Sandbox Code Playgroud)\n

但是当我将大小增加到时10,请参见下面的示例,输出看起来很奇怪:

\n
(9 29 49 13 0)\n
Run Code Online (Sandbox Code Playgroud)\n
(0 0 0 0 0 -1 0 0 0 0) # expectation was (9 29 49 13 0 0 0 0 0 0)\n
Run Code Online (Sandbox Code Playgroud)\n

在 C 中时:

\n
#define MATHLIB_STANDALONE 1\n#include "Rmath.h"\n#include <stdio.h>\n\nint main(int argc, char** argv) {\n    int draws = 100;\n    int classes = 5;\n    int vals[classes];\n    double probs[4] = {0.1, 0.3, 0.5, 0.1};\n\n    set_seed(123, 456);\n    rmultinom(draws, probs, classes, vals);\n\n    for(int j=0; j < classes; j++) {\n        printf("Count of class %i drawn: %i\\n", j, vals[j]);\n    }\n    return 0;\n}\n
Run Code Online (Sandbox Code Playgroud)\n
Count of class 0 drawn: 9\nCount of class 1 drawn: 29\nCount of class 2 drawn: 49\nCount of class 3 drawn: 13\nCount of class 4 drawn: 0\n
Run Code Online (Sandbox Code Playgroud)\n

我的 Raku 代码中的实现有什么问题吗raku_rmultinom

\n

系统信息: \n这是在 Windows 10 64 位中完成的。为了重现性,我在这里提供了标头和动态库:\n https://replit.com/@sumankhanal/rakunativecall

\n

要使用同一文件夹中的 Rmath.h 头文件和库来测试 C 文件,请移至该目录并从cmd以下行运行:

\n
gcc -I. -L. <cfile> -lRmath\n.\\a.exe\n
Run Code Online (Sandbox Code Playgroud)\n
\n

否则,我是这个包https://github.com/sumanstats/Statistics的作者,该包raku_rmultinom在全球范围内可用,任何人都可以安装和测试输出及其行为。

\n
\n

使用的Raku版本是从https://rakudo.org/dl/rakudo/rakudo-moar-2023.09-01-win-x86_64-msvc.zip下载的; raku -v给出:

\n
Welcome to Rakudo\xe2\x84\xa2 v2023.09.\nImplementing the Raku\xc2\xae Programming Language v6.d.\nBuilt on MoarVM version 2023.09.\n
Run Code Online (Sandbox Code Playgroud)\n

rai*_*iph 1

我认为有必要总结一下这个 SO 中发生的事情。

首先,@Suman(提问者)得出的结论(在他们的问题下面的评论中)是答案“看起来像”:

@prob.append(0 xx $size - @prob)
Run Code Online (Sandbox Code Playgroud)

这反映了这样一个事实:对于这个 Q,有时会出现弥合 C 和 Raku 之间差距的情况,问题是 Raku 规范(在 Raku 中编码时通常有意义)和 C 规范(在用 Raku 编码时通常有意义)之间不匹配。 C)而不是双方技术的问题。评论交流中的一些摘录说明了深入了解这种不匹配的过程:

在 Ubuntu 上测试...输出如下:(9 29 49 13 0 0 0 0 0 0)

Raku 是动态语言,C 是静态语言。您需要非常小心数组大小。没有问题,4因为它正是数组的大小@prob

在 Raku 部分,当 时$size > +@prob,您正在调用未定义的行为...补救措施可能是确保@prob有足够的带有0s 填充的元素,例如,通过@prob.append(0 xx $size - @prob);in raku_rmultinom

在C中,当你声明一个变量但没有初始化它时,它将被赋予一个“垃圾”值,即,无论值恰好位于指定给该变量的内存地址处。对于数组,它的所有元素都将具有谁知道什么值,例如您观察到的1635326112327580。另一方面,在 Rakudo 的 NativeCall 中,该.allocate方法恰好用0s 显式初始化数组元素,因此您拥有所有0s (除了 s -1)。