创建用 Python 实现的 JSL 函数最近,我做了一个关于 Python 技巧和窍门的内部 JMP 演讲。我认为有一个技巧特别有用,所以我想与大家分享:创建一个 JSL 函数来包装对 Python 函数的调用。换句话说,这是可以用 Python 编写但具有 JSL 接口的实用程序代码。为了便于说明,我实现了一个简单的 Python 函数,该函数接受两个对象并将它们加在一起,然后将该调用包装在 JSL 函数中。我从三种不同类型的数据(数字、字符串和列表)和一个将两个 Python 对象加在一起的函数开始。
Names Default to Here(1);
one = 1;
two = 2;
author = "Paul Nelson";
devel = "Developer: ";
list1 = { 1, 3, 5, 7 };
list2 = { 2, 4, 6, 8 };
Python Submit("\[
def plus(a,b):
print(a, b)
return a+b
]\");
任何支持“+”运算符的 Python 对象都可以由此 Python 函数处理。
// Python Execute is an underutilized gem. You can send a list
// of inputs and get a list of results and execute code all in one
// JSL statement.
Python Execute( {one, two}, {result}, "result = plus(one,two)" );
show(result);
结果:
result = plus(one,two)
/*:
1.0
2.0
result = 3;
这就是有趣的地方!特别是,一个 JSL 函数包装了 Python 定义的 plus() 函数。
// create a JSL function that wraps the the Python function
blow_mind = Function( {first, second}, // parameters
{pyresult}, // local variables
Python Execute( {first, second}, {pyresult}, "pyresult = plus(first,second)");
Return( pyresult ); // value to return
); // end of the definition
// Call the Python function through our JSL function. Good way to extend JSL
// using code written in Python. Could be useful for add-ins.
// Notice the same Python function handling different types of Python objects.
show( blow_mind(devel, author) );
show( blow_mind(list1, list2) );
给出结果:
//:*/
pyresult = plus(first,second)
/*:
Developer:
Paul Nelson
blow_mind(devel, author) = "Developer: Paul Nelson";
//:*/
pyresult = plus(first,second)
/*:
[1, 3, 5, 7]
[2, 4, 6, 8]
blow_mind(list1, list2) = {1, 3, 5, 7, 2, 4, 6, 8};
我们简单的 plus() 函数处理数值求和以及字符串和列表的连接,这些都可以作为 JSL 函数调用。其余 JSL 代码完全不知道 Python 中实际调用了什么。
附有完整文件。
您希望继续使用 JSL 编写代码,但对于某个特定函数,您知道有一种简单的方法可以用 Python 编写。那么如何创建一个用 Python 实现其功能的 JSL 函数呢?
自动化和脚本回复:创建在 Python 中实现的 JSL 函数感谢@Paul_Nelson对 Python 支持的改进!
使用 Python Selenium 编写的浏览器脚本显示了这一点
// 1: navigate to jmp.com
nav = Function( {url}, {rc},
Python Execute( {url}, {rc},
"\[
try:
driver.get(url)
rc = "ok"
except Exception as e:
rc = repr(e)
]\" );
return(rc);
);
rc = nav( "https://www.jmp.com/" );
if( rc != "ok", throw("nav: "||char(rc)));
最后两行通过包装函数调用,使网络浏览器导航到 JMP 网站。JSL 函数的工作方式与 Paul 的示例完全相同。
回复:创建在 Python 中实现的 JSL 函数具有多个返回值的全局、多行正则表达式搜索
Add Custom Functions(
New Custom Function(
"hogi",
"Regex",
Function({input, pattern},
SubstituteInto(input,"\!n"," ","\!r", " ") ;
Python Send( input );
Python Send( pattern );
error = Python Submit( "\[
import re
print(input)
matches = re.findall(pattern,input)
]\" );
matches = Python Get( matches );
),
<<DESCRIPTION></DESCRIPTION>回复:创建在 Python 中实现的 JSL 函数关于@hogi的注释,我相信Function()应该有一个return(matches);语句。
另外,使用 Python Execute() 也无需执行发送和获取操作。
Add Custom Functions(
New Custom Function(
"hogi",
"Regex",
Function({input, pattern},
SubstituteInto(input,"\!n"," ","\!r", " ") ;
Python Execute( {input, pattern}, {matches}, "\[
import re
matches = re.findall(pattern,input)
]\" );
return( matches);
),
<回复:创建在 Python 中实现的 JSL 函数啊,对了。Python Execute好用多了 
在 JSL 函数中,最后一个表达式是返回值 - 即使没有括住return() 。
回复:创建在 Python 中实现的 JSL 函数我不擅长 Python,但有一些 Python 库(如 xxhash)我想使用。
我对内存使用情况有疑问。如果我以这种方式用 JSL 函数包装一些 Python,并且在包装的 Python 代码中创建了一个 Python 对象,那么每次调用该函数时,对我的 JSL 包装函数的调用是否会创建一个 *新的* 持久 Python 对象(例如,如果我调用该函数数千万次,是否会耗尽内存)?
如果我在我的(例如)jmpstart.jsl 代码中创建一个命名的 Python 对象,并在我的过程内部引用该对象,会怎么样?这样会更好吗?
下面是我想要使用 xxhash 执行的操作的示例。(请注意,我在 jmpstart.jsl 脚本中将 xxhash 导入 Python 环境。我只能从此函数发出 JSL 浮点数,因为 JSL 不支持 8 字节整数……)
Add Custom Functions(
New Custom Function(
"Global",
"xxhash64",
Function( {str},
{digest},
Python Execute( {str}, {digest}, "digest = xxhash.xxh64( str ).hexdigest()" );
Return( Hex To Number( digest ) );
)
)
)
回复:创建在 Python 中实现的 JSL 函数PS。我注意到这是糟糕的代码,因为十六进制摘要是无效 IEEE 浮点数的概率约为 0.8%,导致该过程返回 JSL 缺失值。话虽如此,我仍然对内存管理感到疑惑。
回复:创建在 Python 中实现的 JSL 函数hashlib 和 base64 都是内置的 Python 库。
我昨天本来想专门研究这个案例。我可以告诉你,最坏的情况是,在 JMP 运行的整个过程中,它应该“泄漏”一个对 digest 的引用。当 JMP 关闭时,该变量将在 Python 关闭时释放。Python Set() 代码创建digest变量并将其放在 Python 环境的 globals() 字典中。这允许 Python Get() 能够按名称查找值并将值返回给 JSL。Python Execute() 基本上是 Python Send/Set()、Python Submit()、Python Get() 的便利函数。因此字典有一个对digest的引用,但是一旦 Python Execute() 超出范围,字典应该是唯一持有引用的字典。在第二个循环中,该值被新值覆盖,因此应该释放旧引用,从而允许垃圾收集返回该内存。
回复:创建在 Python 中实现的 JSL 函数谢谢保罗。关于内存,这正是我所希望的。我会看看 hashlib 和 base64。
背景是:我想要一个 8 字节哈希,我编写了一个使用 JSL 原生 MD5 函数并将其截断为 8 字节的哈希,但使用我的字符串输入,哈希似乎最终被聚集在一起。有人向我推荐 xxhash64,因为它可以提供非常随机分布的哈希值。所以我使用了它,它似乎给出了分布良好的哈希。
如上所述,xxhash64 可以发出 longint,但我无法将其传输到 JSL 中。我可以将结果作为 JSL BLOB 传输,但 JMP 不允许我将 BLOBS 用作 LINK ID——而这正是我想要使用哈希的原因。这与原始问题有点偏离,但如果您有任何想法,我们将不胜感激。
回复:创建在 Python 中实现的 JSL 函数PS. 可以只使用十六进制的字符串值作为我的链接 ID,但我渴望一些小而快的东西。我正在处理约 8000 万行的表格。
回复:创建在 Python 中实现的 JSL 函数@john_madden JMP 的 64 位双精度数字可以保存从 -2^52 到 2^52 的有符号整数。我认为您可以使用 hextonumber 保留 16 个十六进制数字哈希中的 13 个十六进制数字,并避免任何有关无效浮点数的问题。但 52 位可能不够。
我也在这个社区玩过,发现这个生日攻击表很有用。我正在使用文件的哈希值来制作唯一密钥,想知道我需要多少位。从表中,我看到将 6,100,000 个项目添加到 64 位哈希表中,发生冲突的概率为 1/1,000,000。
对于我的用例,文件服务器的磁盘是 1e11,典型文件是 1e4,或者限制为 1e7 个文件。64 位哈希太小,96 位就可以了。我当然选择了 256...

回复:创建在 Python 中实现的 JSL 函数嗨 Craig。太棒了!你对 256 位值使用了哪种 JMP 类型?
顺便说一句,如果开发人员能够在某个时候向 JSL 添加比 MD5 更现代的哈希函数,那就太好了。
回复:创建在 Python 中实现的 JSL 函数这是我最近的尝试,似乎效果不错:
"Accepts a string as input, and returns a JSL floating point number that encodes a 'nearly'-64-bit hash of the string. The hash is generated using the Python xxhash package with the default (0) seed.<BR />Python returns the 64-bit hash as a 16-character hexadecimal string. If the bits 2-12 are 11111111111, they are changed to 01111111111 before being processed further by JSL.<BR />This avoids generating an IEEE 754 floating point value of NaN or ∞, which JSL would interpret as Missing.<BR />This trick cuts the number of possible floating point exponent values<BR />from 2048 to 2046, resulting in a hash with 2^63 * 2046 possible values (= 1.89e22).<BR />For 10 million different inputs, this should yield a collision probability of roughly 1 in 380 milion<BR />according to Craige's reference if I calculcated right. On my M3 Max MacBook Pro, the routine takes around 26 μsec per iteration with random strings of length 11.
Names Default To Here( 1 );
Add Custom Functions(
New Custom Function(
"Global",
"xxhash64",
Function( {str},
{digest},
Python Execute( {str}, {digest}, "digest = xxhash.xxh64( str ).hexdigest()", echo( 0 ) );
startChars = Substr( digest, 1, 3 );
Hex To Number(
Match( startChars,
"7ff", "3ff" || Substr( digest, 4, 13 ),
"fff", "bff" || Substr( digest, 4, 13 ),
digest
)
);
)
) << Description(
"Accepts a string as input, and returns a JSL floating point number that encodes a 'nearly'-64-bit hash of the string.
Python must have already been initialized as follows:
Python Execute( {}, {},
\!"
import jmputils
jmputils.jpip('install', 'numpy pandas scikit-learn xxhash')
import jmp
import numpy
import pandas as pd
import xxhash
\!""
) //
<<PROTOTYPE></PROTOTYPE>回复:创建在 Python 中实现的 JSL 函数我的猜测是 Craige 正在使用十六进制数字或 base64 编码数字的字符串。
Python 集成的一大优点是,您不需要 JMP 来“赶上”并提供更现代的哈希函数。您可以通过 Python 获得强大功能。我提到 hashlib 和 base64 的原因之一是它们是 Python 标准库的一部分,因此所有 JMP 用户都可以使用它们而无需安装外部包。
回复:创建在 Python 中实现的 JSL 函数@约翰马登 - you want <SPAN class="token number">2</SPAN><SPAN class="token operator">^</SPAN><SPAN class="token number">52</SPAN> <SPAN class="token operator">*</SPAN> <SPAN class="token number">2046</SPAN> rather than <SPAN class="token number">2</SPAN><SPAN class="token operator">^</SPAN><SPAN class="token number">63</SPAN> <SPAN class="token operator">*</SPAN> <SPAN class="token number">2046</SPAN> . Or just use the 64 bit row.
很久以前,我就知道 JSL 字符串是如何保存在内存中的;我记得在 24 个字符左右时性能发生了显著变化。您可能希望使用 96 位版本,并确保它保持在 24 个十六进制数字或以下。对于 10,000,000 个字符串,空间在您的 16 GB 机器上为 1/4 GB……并且您遇到错误的概率从 1e-6 上升到 1e-15。如果不是运气不好,我根本就没有运气 - https://quoteinvestigator.com/2020/09/02/bad-luck/ 。
I'm working with python/django/postgresql currently and I store the hex digits as a database key...a 64 character string gives me the 256 bits.
回复:创建在 Python 中实现的 JSL 函数再次感谢 Paul 和 Craige。顺便说一句,Python 集成让我兴奋不已,现在想尝试学习 Python,因为这里的 IT 同事想了解我的一些 JSL 代码,并且总是问我 Python 的对应代码是什么。非常感谢 Paul 所做的所有工作。
我还需要纠正上面脚本中哈希值数量的计算 - 我认为它更像是 2^64 - 2^63,比我说的要少很多。
关于存储哈希值,你说服了我不要那么装腔作势。我想你让我相信,最终只存储十六进制字符会更好。我必须尝试在大型表上进行一些连接,看看我在十六进制字符串列或浮点数列上进行连接是否真的会对速度产生影响;或者我是否使用字符串或浮点数作为链接 ID。
如果我将我的大型 JMP 表导出到 RDB,我可以将它们转换为长整型。
回复:创建在 Python 中实现的 JSL 函数Craige 关于哈希值的数量,您完全正确,我又感到困惑了,而且没有读到您的帖子。2^52 * 2046 是正确的。