挑战 5 – 结果对于这篇文章的延迟,我深表歉意。 除其他事项外,我一直非常忙于准备关于函数式数据浏览器的演讲在JMP探索峰会。 看来常规贡献者同样全神贯注,因为我只收到了两份针对此挑战的非工作人员提交的材料。
以下是第五次挑战后的总得分:
说到探索峰会,我的同事斯坦·科普罗夫斯基正在组织JMP 脚本取消会话。 请点击链接了解更多详情;我们很高兴在那里见到你。
在挑战 5 中,我要求你编写元函数,即函数的函数。 我的动机是采用只能应用于单个数字或字符串的函数并扩展它们,以便它们可以应用于它们的复合值、向量和列表。 我发现这种类型的操作有些规律地发生,并且当 JSL 函数尚不存在时通常可以使用数据表公式来实现。 我希望在数据表之外也能使用此功能。
第 1 项和第 2 项应该相对简单。 使用向量时,依赖 JSL 已通过预先存在的 JSL 函数或矩阵运算实现的功能会很有帮助。 计算中值绝对偏差 (MAD) 最简单的方法是一行代码:
中位数(绝对值(内维克- 中位数(内维克) ) )
在哪里内维克是输入向量。 该解决方案依赖于了解可以从内置 JSL 函数正确生成中位数。 该代码直接来自 MAD 的定义。
计算截尾均值有点棘手,并且可以深入了解提供精确函数定义的重要性(稍后会详细介绍)。 一种常见的方法是计算与以下项相关的分位数之间界限值的平均值低百分比和百分比高,在哪里低百分比和聚氯乙烯是挑战 5 帖子中定义的参数。 例如:
loQ = 分位数(低百分比,内维克);
hiQ = 分位数(1-聚氯乙烯,内维克);
意思是(内维克[Loc(loQ <内维克< hiQ)]);
然而,该解决方案修剪了不多于低百分比从低值和不多于聚氯乙烯来自高价值。 因此,修剪的总百分比可能小于低电平+聚氯乙烯但再也不会了。 这种方法与发现的方法不一致分配平台不仅从两个尾部均匀修剪,而且更重要的是,保证修剪的总百分比至少请求的金额(可能更多)。 以下代码产生与此定义一致的结果:
loRank = 上限(百分率*N行(内维克));
hiRank = 上限((1-聚氯乙烯)*N 行(内维克));
goodVals = (loRank < 排名(内维克) <= hiRank);
好瓦尔斯`*内维克/Sum(goodVals);
对于第 3 项,我正在寻找一个函数,该函数可以查找字符串列表中每个项目中给定字符串的所有出现情况。 如果给定的字符串是“a”并且字符串列表
{“跟骨”,“狐狸”,“宝贝”}
返回的值是
{[2, 5, 9, 13, 15, 17],.,[2]}
措辞正确的示例将有助于更准确地定义函数。 我的循环解决方案:
listContains = Function({listIn,valueString},{i,outList,nextMat,start},
outList = Repeat({.},N Items(listIn));
For(i=1,i<=N Items(listIn),i++,
nextMat = [];
start = 1;
While(start = Contains(listIn[i],valueString,start),
nextMat = nextMat |/ start;
start+=Length(valueString);
);
If(N Row(nextMat) > 0,outList[i] = nextMat,outList[i]=.);
);
outList;
);
处理挑战 1 中从字典中随机选择的 100 万个单词的列表需要 2 到 3 秒的时间。
我的动力应用函数是复制我在其他编程语言中使用的功能,在其中我可以将函数应用(或映射)到聚合数据结构(通常是字符串或矩阵列表)。 我希望能够使用类似的东西:
长度(字符串列表)
获取列表中每个项目的长度,而不是使用循环或列公式。
发布挑战后,我意识到有些细微之处我没有考虑到,例如该函数可以应用于输入列表中的单个项目或整个输入列表的情况。 此外,我没有提及是否可以将不同的函数参数应用于每个列表元素。
考虑到这一点,并与挑战 5 条目中的评论保持一致,实施应用函数我提供了四个参数:
列表输入– 一个或多个数字、字符串、向量或字符串列表的列表。
号– 要应用的函数的引用名称列表输入。 该函数可以是内置的或用户提供的。
参数列表– 参数列表号之后出现的列表输入或参数列表的列表。 如果提供了列表列表,则它必须具有与以下相同的基数:列表输入。
旗帜- 1 如果号应用于列表的每个元素,如果应用于整个列表则为 0。
一种简单但通用的方法是使用表达式和循环构建函数。 在伪代码中:
If flag == 1 then
If argList is a list of lists then
For each item in listIn/argList, build an expression for fn
Else
For each item in listIn, build an expression for fn using the same items from argList
Else flag == 0
Build one expression for listIn and argList
这种方法的代码可以在applyFunction(循环).JSL 。 @大卫_伯纳姆提供了一种递归方法Challenge_5_burnham.JSL 。 虽然很优雅(谁不喜欢递归?),但如果递归太深,它就会遇到问题。
试图使应用函数无循环函数被证明是一个相当大的挑战。 主要问题是不知道里面的物品数量参数列表先验(或者更具挑战性的是,处理数字变化的情况)。起初,我希望能够通过巧妙地使用表达式求值来实现这一目标。 例如,如果函数只需要一个参数,则以下内容将起作用:
applyFunctionSimple = Function({listIn,fn,argList},{i,fnExpr,outList},
i = 1;
outList = {};
outList[1::N Items(listIn)] = Substitute(
Expr(func(in,arg)),
Expr(func),Parse(fn),
Expr(in), Expr(listIn[i++]),
Expr(arg), argList[1]
);
Eval List(outList)
);
的魔力代替首先生成一个长度列表N 项(列表输入)列表中的每个元素都是表达式号(列表[i++],精氨酸1 ) , 在哪里号和精氨酸1在函数调用中给出列表[i++]是字面意思。 什么时候评估清单应用于外列表,然而,每个我给出一个单独的顺序值,并对列表项进行评估,产生所需的结果。 我从以下网站学到了这种使用表达式的方法@约瑟夫_摩根谁,连同@drewfoglia / @EvanMcCorkle已经提供了涵盖该主题的优秀 Discovery 教程。 教程可以找到这里和这里。
不幸的是,我无法弄清楚如何完全使用这种方法,并且不得不依赖一种我试图避免的技术,因为它效率低下且不优雅,通过字符串操作构建通用函数,然后使用表达式求值来获取输出。 我的代码在applyFunction(无循环).JSL 。 虽然这段代码保留了大部分的风格应用函数简单,正则表达式用于从表达式中去除大括号参数列表。 幸运的是, @Craige_Hales提供了一个纯粹基于表达式的解决方案克雷格5.JSL 。 由于它是在后来的挑战 5 澄清之前发送给我的,因此它适用号到每个元素列表输入仅有的。 此外,每个函数调用都使用相同的参数集。 该解决方案和我的解决方案都存在相同的问题,即比我们基于循环的解决方案慢(2-5 倍之间)。
希望这一切都有道理。 如果没有,或者您有其他见解,请在下面发表您的评论。

函数来运行函数? 如何? 为什么?
JMP 博客回复:挑战 5 – 结果@ErraticAttack - 好的!
回复:挑战 5 – 结果谢谢@ErraticAttack 。我喜欢你的做法!
回复:挑战 5 – 结果只是在这里添加我的两分钱,因为我正在构建一些函数式编程类型的辅助函数。
根据我的经验,很容易变得有点忘乎所以并构建一个过于复杂的解决方案,该解决方案既有点慢又有点难以理解。
因此,这是一个快速简单的映射/过滤/减少的实现,它几乎与内置方法(循环或直接内置调用)一样快,并且具有不需要使用 Expr(...) 函数的额外好处来利用它们。
map = Function( {inputs/* list, function*/},
/* uses a single underscore _ as the wild-card */
{__i__, __result__, __list__, _},
__result__ = {};
__list__ = Arg( inputs, 1 );
Eval(
Substitute(
Expr(
For( __i__ = N Items( __list__ ), __i__ > 0, __i__--,
Insert Into( __result__, _ = __list__[__i__]; __function__, 1 )
)
)
,
Expr( __function__ ), Arg( inputs, 2 )
);
);
__result__
);
reduce = Function( {inputs/*list, function, seed = 0*/},
/* uses a single underscore for current item, 'acc' for current value */
{__i__, __list__, _, acc = 0},
If( N Items( inputs ) == 3,
acc = Arg( inputs, 3 )
);
__list__ = Arg( inputs, 1 );
Eval(
Substitute(
Expr(
For( __i__ = N Items( __list__ ), __i__ > 0, __i__--,
_ = __list__[__i__];
acc = __function__
)
)
,
Expr( __function__ ), Arg( inputs, 2 )
)
);
acc
);
filter = Function( {inputs/*list, filter = _*/},
/* uses a single underscore _ for current item */
{__i__, __list__, __result__, _filter_, _},
__result__ = {};
If( N Items( inputs ) == 1,
_filter_ = Expr( _ )
,
_filter_ = Arg( inputs, 2 )
);
__list__ = Arg( inputs, 1 );
Eval(
Substitute(
Expr(
For( __i__ = N Items( __list__ ), __i__ > 0, __i__--,
_ = __list__[__i__];
If( __filter__,
__result__[N Items(__result__) + 1] = _
)
)
)
,
Expr( __filter__ ), Name Expr( _filter_ )
);
);
Reverse( __result__ )
);
filter({{1, 0, 2, Empty(), 3, .}});
lst = repeat( {{1, "a"}, {2, "b"}, {3, "c"}, {4, "d"}}, 10000 );
start = HP Time();
a = filter({lst, _[1] < 3} );
end = HP Time();
Write( "\!NMy Method: ", (end - start) / 1000000 );
reduce( {{"a", "b", "c"}, Uppercase( _ || acc ), seed = ""} );
list = Repeat( {Random Integer( 1, 100000000 )}, 50000 );
start = HP Time();
Show( reduce( {list, If( _ > acc, _, acc)} ) );
end = HP Time();
Write( "\!NMy Method: ", (end - start) / 1000000 );
start = HP Time();
Show( Max( list ) );
end = HP Time();
Write( "\!NBuiltin Method: ", (end - start) / 1000000 );
start = HP Time();
Show( reduce( {list, acc + _} ) );
end = HP Time();
Write( "\!NMy Method: ", (end - start) / 1000000 );
start = HP Time();
Show( Sum( list ) );
end = HP Time();
Write( "\!NBuiltin Method: ", (end - start) / 1000000 );
strings = Repeat( {"abcdefg"}, 1e5 ); // about a second
start = Tick Seconds();
substrings1 = map( {strings, Substr( _, 1 + 2, 1 )} );
stop = Tick Seconds();
Write( "\!n applyfunction time: ", stop - start );
start = Tick Seconds();
substrings2 = {};
For( i = N Items( strings ), i > 0, i--,
Insert Into( substrings2, Substr( strings[i], 3, 1 ), 1 )
);
stop = Tick Seconds();
Write( "\!n JSL loop time: ", stop - start );
If( substrings1 == substrings2,
Write( "\!nOK" ),
Write( "\!noops" )
);当我在我的(相当慢的)笔记本电脑上运行它时,我得到以下输出:
/*<BR />My Method: 0.152577
reduce({list, If(_ > acc, _, acc)}) = 99994198;
My Method: 0.056897
Max(list) = 99994198;
Builtin Method: 0.024798
reduce({list, acc + _}) = 2495626010963;
My Method: 0.04259
Sum(list) = 2495626010963;
Builtin Method: 0.007765
applyfunction time: 0.166666666656965
JSL loop time: 0.0500000000029104
OK<BR />*/请注意,使用reduce 函数查找位列表(50,000 个条目)中的最大数字仅花费内置Max() 调用的约2 倍时间,而同一列表的总和则慢约6 倍。考虑到 Map/Reduce/Filter 函数增加的灵活性,这些数字相当不错。
回复:挑战 5 – 结果谢谢@唐麦考马克获取 MFR 的链接。 我不想破坏任何(近期)未来的公告,但是是的,它即将到来......
回复:挑战 5 – 结果谢谢@唐麦考马克为了详细的写! 这个挑战让我想起了伟大的事情MFR:用于列表/关联数组操作的 JSL 函数编程库乙y @nascif_jmp 。 希望其中一些构造有一天能够进入 JSL 的基本函数中。