J打高尔夫的秘诀


33

GolfScript经常走自己的路,我觉得J里打高尔夫球的便捷提示集可能会有助于对抗邪恶帝国。您有什么技巧来使这种已经很简洁的语言更短?

对于那些想学习J的人来说,显而易见的起点jsoftware站点,尤其是词汇表Learning J指南和J for C程序员指南。


1
GolfScript gets its own way far too often在2019
不相关的字符串

Answers:


14

要挤出J的最后几个字符,有很多技巧。下面,假定每个大写字母都是原始动词(即,我要删除空格,否则会用空格分隔名称)。

  • 当您要坐火车时,您需要在途中的另一个应用上面的功能,([:FLGR)并且(LF@:GR)具有相同的字符数,但是(LF@GR)保存一个。如果G的帧大于或等于F的单声道秩,则这是有效的变换。值得注意的是,所有列车有无限的排名,因为这样做, ,. ,: ~. /: \: [ ]和大多数使用#|.

  • 如果您必须从列表中挑选字符串,并且这些字符串没有空格,请使用>i{ab`cd`ef。它很脏,但是它会为您必须处理的每个新字符串保存字符,除非您只是提取单个字符,而且字符列表的长度必须短于4。发生的事情是未定义的名称被视为对动词的引用,当您使用这些动词的动名词时,会得到一个带框的名称字符串。任何已经定义为名词,副词或连词类型的名称都不能以这种方式使用,因为这些名称必须先解析后`才能使用。

  • 如果您很幸运能够使用一个表达式而不只是一个默认的动词,那么将重用的任何位分配给变量(名词,动词或副词)几乎总是值得的。括号有时会通过将其正确地放到您之前有空格的位置来偿还,大多数这样的定义如果再一次被使用都是值得的。

  • 这样的连词(FGH)^:(u`v`w)可以重写u`v`w(FGH^:)。这适用于任何长度的火车,甚至1,尽管您只保存了任何技巧,只要此技巧从正确的参数中删除了括号。仅当您预加载左操作数时,此技巧才有效。(不知道发生了什么?查找“默认副词”,并研究J词典的“ 解析和执行”部分。)

  • 不要使用a.&i.,使用u:{&a.3&u:是等效于长度,虽然,前者可能是一个结合更实用的(取决于一起)。

  • 类似(2%~F)(F%2:)等长的事物。这很有用,因为有时,根据火车其余部分的样子,您可以使用@第一点上写的技巧重新构造它,以节省一些绝望的角色。(当然,如果F],而火车是单子,则使用%&2保存字符,du。)

  • 像钩子一样的火车,带有][作为最左边的动词,例如(]FGH)

    • ]使您可以分解二元应用程序并仅使用正确的参数。(与交换到左边(]FGH)~,至少要损失1个字符,甚至更多。)将一个char保存下来(FGH)@],并且在gerunds中非常方便!
    • [单调应用的钩子中,您可以对右侧的副作用进行某些处理,然后再次返回该参数。最常见的用法是1!:2,可能是格式化垃圾。
  • I / O很烂。通过尽一切可能循环来加快流程。例如,1!:1具有rank 0,并且都1!:2 3具有rank _ 0,因此可以通过制作1的数组并1!:1直接在它们上面运行来利用此函数。请注意,它".也具有等级1,因此您通常也可以直接将其放在之后1!:1,而不必通过@或将其附加到名称上。

  • 寻找放置此位置的位置并不容易,但::很有用。

    • ::]^:_例如,它是一种特别强大的组合,使您可以做一些危险的事情,直到无法再做为止。(需要遵守通常的^:_警告)。

    • 这也使您可以{在没有所需索引的列表上使用,因为发生这种情况时会引发域错误。例如,仅当列表的头部存在时才有用(尝试使用::]来返回空列表或::_1:返回错误代码,依此类推)。

  • ]`($:@u)@.v通常可以使之比短u^:v^:_,尤其是在uv可以一起使用的定义上。类似的情况下,保存条件样u^:(1-v)]`u@.v。考虑一下您的选择,尤其是当您有很多命名动词在浮动的时候。它也稍微灵活一些,但是请记住,如果使用$:,则存在递归深度,很容易碰到。(通常是1800次迭代吗?)


不利的把戏真的很酷。
FUZxxl 2015年

“保存一些绝望的角色”当您学习灯光时,一切看起来都像是转移的绰号!:)
Soham Chowdhury 2015年

1
“使用%&2可以节省字符,char。” -:节省另一个!
林恩

11

在J中打高尔夫球时,最重要的是不仅要了解问题,而且还要将问题简化为一系列数组转换。您需要了解这种思维方式,才能使J代码成功。

例如,最近的挑战要求解决最大的子阵列问题解决这个问题的stock算法是Kadane的算法,具有以下非正式描述:

遍历该数组并在每个位置处找到在此结束的最大子数组的总和,即0的最大值或当前索引处的值加上在先前位置结束的最大子数组的总和。在查找整个数组中最大的子数组时,请计算这些子数组的最大值。

转换为命令性代码很简单:

  1. 设A为输入数组。
  2. hmi ←0。
  3. 如果 ≥LEN(A)返回
  4. h ←max(0,h + A [ i ])。
  5. m ←max(mh)。
  6. + 1。
  7. 转到 3。

乍一看,此算法对于J而言似乎很复杂,因为存在一个显式循环,起初看起来并不像归约。如果您知道算法在做什么,则可以解开各个步骤,然后看它实际上执行了两个简单的数组操作:

  1. 扫描通过阵列来计算每个索引在结束最大的子阵列的长度。
  2. 使用max函数减少这些长度以找到最大值。

现在,这两个步骤很容易在J中实现。下面是一个翻译:

  1. (0 >. +)/\. y , 0–此步骤从数组的另一端开始,以更好地适应J的范例。0 >. +是默契的0 >. x + y
  2. >./ y

综上所述,我们得到了算法的非常简洁的实现:

>./ (0 >. +)/\. y , 0

如果您学习这种实现算法的方法,那么您的解决方案将像这段代码一样简洁。

这是我随着时间积累的一些技巧。随着我对J打高尔夫球的更多了解,此列表将得到扩展。

  • 学习字典。它包含许多真正晦涩的动词,直到您看到它们有用时,它们才有意义。例如,monadic =最初很奇怪,但在ASCII艺术挑战中非常有用。
  • &当您需要强力连词时,请在默认上下文中使用二进位。词汇暗示u@[&0是我的默认替代,4 : 'u^:x y我也是。
  • 在许多情况下,您可以避免[:@:依次u@v选择一个u带有左参数的变体来避免或。例如,要删除结果的第一项,则由于某种原因而不能v使用if 。1}.v[:}.v}.@v
  • ] v通常比v@]您想v在二元环境中使用单子函数要短。这很有用,尤其是在v动词很长的情况下。
  • 有时您可以写m (n v w) y而不是(n v m&w) y。这可以避免空格和括号。
  • #\代替>:@i.@#
  • u &. vv有正面时很有用。否则,您可能想要使用[: vinv u & vu & (v :. vinv)代替。
  • 了解等级及其使用方法。尝试摆弄等级衔接,直到找到合适的东西。它有助于了解等级如何影响您的代码。
  • ^:_ 对于要达到收敛的算法(如洪水填充或模拟)非常有用。
  • 了解您的标准库。它包含非常有用的功能,可以节省大量字符。
  • 该copulæ =.并且=:可以在任何地方语被嵌入。使用此选项可以在默认标记还不够的情况下制作单线。
  • ,简化多维数组时,请使用monadic 而不是多次简化。
  • 了解当挑战强加运行时边界时,特殊代码支持哪些短语。一些有用的东西反常地以O(n)而不是O(n 2)运算。
  • 盒子对树木很有用。

J是否足够聪明,可以通过缓存重复使用的计算在O(n)中运行您的max子数组解决方案,或者它会做直接的事情并在O(n ^ 2)中运行它?
约拿(Jonah)

@Jonah我认为它运行于二次时间。
FUZxxl

10

警惕使用循环。

虽然j具有循环结构(for. do. end.while. do. end.而变化),如果你发现自己使用它们有一个可能性,你的算法是不是在玩到J的高尔夫球场的优势和有性格的储蓄作出。

^:强权联合是你的朋友。要执行动词x时间:

verb^:x

如果您需要列表中每个迭代的结果:

verb^:(i.x)

您还可以^:用来有条件地执行动词:

  +:^:(3<])"0[ 1 2 3 4 5 6
1 2 3 8 10 12

+:如果^:该项目是大于3 3<](中"0,因此在工作时间的项目改变了动词的排名)。


装箱的值类似于(i.x)示例,即f^:(<x)等效于f^:(i.x)
FireFly 2014年

9

输入值

1!:1[1 将按Enter键终止一行输入。

1!:1[3 将需要多行输入(在Mac上为Ctrl-D,在Windows为Ctrl-C终止)。

如果您尝试输入数字,则使用".将评估字符串并返回一个可供操作的数字列表。如果您要输入一个数字,但需要单独对数字进行运算,".,.(感谢Jan Dvorak的评论),或"."0将字符串拆分为单独的数字:

   "."0[1!:1[1
12345
1 2 3 4 5

   ".,.1!:1[1
12345
1 2 3 4 5

如果您正在阅读字符串,则获取单独字符串的盒装列表的最短方法是使用;:。这最适合用空格分隔的字符串:

   ;:1!:1[1
hello world
┌─────┬─────┐
│hello│world│
└─────┴─────┘

出于好奇,(我只和J玩了一点)什么会1!:1[2成为(如果有的话)?
加菲2012年

从我可以在1!:页面上收集到​​的信息(我不是J专家)2是屏幕,因此从screen输入没有多大意义。
Gareth

感谢您的链接。从那里开始,实际上看起来2是无效的吗?我目前没有J电脑可以试用。我看到的地方2在笔记的下方1!:1,是为了1!:2
加菲2012年

@Gaffi输入和输出的文件号似乎是连续的,所以我猜想它们是固定的,而2、4和5仅出现在输出下,因为尝试从它们输入没有任何意义。同去的其他方式为1和3
加雷

由于".排名为1-xx,并且,.始终生成2D数组,因此".,' ',.(用空格缝制,旅行和评估; 8个字符)可以替换为".,.(旅行项目和评估; 4个字符)。
John Dvorak 2013年

6

使用迭代来计算序列

通常,解决OEIS序列挑战将需要使用其页面上给出的公式之一。其中一些适合于J,而另一些则不太适合。递归公式很简单,但是迭代可能并不简单。我开始使用的模式是

(s(]f)^:[~]) n
          ]  Gets n
 s           The first value in the sequence
         ~   Commute the argument order, n is LHS and s is RHS
        [    Gets n
      ^:     Nest n times with an initial argument s
  (]f)         Compute f s
             Returns (f^n) s

where s是序列中的第一个值,f是一个动词,它将根据给定的上一个术语来计算下一个术语,并且n是要计算的术语的从零开始的索引。此方法依赖于以下事实:在计算二元组的功率时,LHS绑定到该二元组以形成一个新的monad,并且monad嵌套在初始值上。赋予副词的二元词是一个钩子,(]f)给出n了LHS 的索引和序列中一项的值s。钩子将适用fs作为一个单子,然后忽略n返回的结果f s

标准库

有时,您可能会发现J在其标准库中支持动词。例如,大多数按位整数运算都绑定到比使用原始调用短的名称。

AND =: (17 b.) NB. it is actually '$:/ :(17 b.)'

日期和时间内置也可用。

范围

如果您有一组值,[a, b, c]并且想要根据其乘积来形成范围,例如[0, 1, 2, ..., a*b*c-1],通常的方法是找到其乘积,然后形成一个范围,该范围可能[:i.*/花费6个字节。一种较短的方法是,@i.使用4个字节,因为它i.可以形成多维数组,同时仍然向上计数,并且对其进行展平将产生等效范围。

连续打印

一种隐式的方式来打印值并继续使用它而无需显式循环,这([echo)适用于单例情况。echo是标准库中的动词stdout,以解释器中使用的相同格式将其内容打印为内容。然后,该钩子使用左[动词传递相同的输入值。

以10为底的整数

获取整数的基数10位的标准方法是10#.inv]花费8个字节,太多了!另一种选择是将其转换为字符串并在0级进行解析,"."0@":这样可以节省一个字节,但是更好的方法是,.&.":,可以节省另一个字节,从而使最终开销为6个字节而不是8个字节。


5

考虑使用显式定义而不是写默认动词;确定3 :''成本的5个字节,但你可以节省很多@@:而且[:这种方式。


5

我见过的一些(相当)常见的技巧

我正在分享一些对我有用的东西。基本上所有这些都是我自己收到的提示,但我没有得到很多好评。

一级和的总和

而不是使用+/@:(FGH)use (1#.FGH)。这意味着将基数降为基数1,这实际上意味着对数组求和。尽管它比更长+/,但并不需要上限或组成,这通常使它比使用短+/

计算尾随真相

如果您有一个布尔值列表,并且想要计算尾随真实数,请使用#.~看这里。该APL答案为是如何工作的一个很好的解释。当然,这只对我有两次帮助,但是我认为我还是会分享的。

在(&。)下

这不是一个特定的技巧,而只是一个一般性的建议:-副词&.-通常会导致优雅的(更重要的是)简短的解决方案。打高尔夫球时请记住这一点。

通常,它对于和其他基本转换挑战很有用,例如,该代码从数字中删除最高有效位:(}.&.#:转换为二进制数字列表,删除第一个数字,然后撤消转换为二进制数字列表并转换返回十进制)。直接的解决方案是再增加两个字节:#.@}.@#:

因为您可以使用,所以Under也对需要使用十进制数字的挑战很有帮助u&.":。例如,将Miles分割为小数位数的简短方法是在下使用:,.&.":

最后一个示例是查找向量的幅度:+/&.:*:,请注意,由于-square的等级为零,因此需要从-square 收集*:带有&.:-under的所有结果*:


4

更短的方法来打乱行列

有时,您会有类似的代码<"0 i.3 3,想要v在rank 上应用动词r。但是,如果使用名词(如0),则通常必须包含一个空格。为避免这种情况,您可以使用另一个u具有相同等级的动词并u"v改为使用。例如,由于+具有等级0 0 0,我们可以使用<"+代替<"0

这是所有动词及其等级的表格(可使用来获得v b. 0):

0 0 0     > + * - % ^ | ! ? <. <: >. >: +. +: *. *: %: ^. j. o. q: r.
0 _ _     -. -: E. i: p:
1 0 1     p..
1 0 _     { A.
1 1 0     p.
1 1 1     #.
1 1 _     C.
1 _ _     ;: ". i. I.
2 _ 2     %.
_ 0 0     = < ~. ~: {: }: ?. L.
_ 1 0     #:
_ 1 _     $ # |. |: {. }. ": {::
_ _ _     , ; [ ] _: $. $: ,. ,: /: \: [: e. s: u: x: 0:

要使用此表,请r在左侧找到所需的排名,然后v从右侧选择合适的动词。例如,如果我需要将动词向量化为vdepth 2 _ 2,那么我会在左侧找到该等级并%.从右侧选择。然后我用v"%.代替v"2 _ 2


3

strings 图书馆:打高尔夫球的秘诀

字符串库对于使用字符串进行操作非常有用。当然,这需要花include'strings'很多钱(考虑到J,这是非常昂贵的),但是您有时可能会获得好处。

stringreplace

使用字符串替换找到自己吗?观察到A stringreplace B与相同B rplc A

实际上,这是如何rplc实现的:

   rplc
 stringreplace~

cuts

cuts因此,该动词提供:

在x处切y(连接)
字符串(动词削减n)文本
  n = _1,但不包括字符串
  n = 1,直至并包括字符串
  n = _2,但不包括字符串
  在字符串之后(包括字符串)n = 2

因此,它实际上是在切字符串。


3

从0到4获取数字

如果在代码中使用数字有限制,请执行以下操作:

0 %_:一除以无穷大。
1 #_:多少个无穷大?
2 #_ _:两个无穷大。
3 verb:有一个内置的。
4 dyad:另一个内置的。

获取数字从10到35

基inifinity文字:11_bb26_bq


3

隐式编程

基本

二元动词

x (F G H) y == (x F y) G (x H y)
x (F G) y == x F (G y)
x ([: G H) y == G (x H y)  NB. G is called monadically

NB. Verbs are grouped from the right by units of 3.
NB. For the following, think like G, I, K are replaced by the results of (x G y) etc.
NB. and then the sentence is run as usual.
x (F G H I J K) y == x (F (G H (I J K))) y
                  == x F ((x G y) H ((x I y) J (x K y)))

NB. Using conjunctions for dyadic verb
x F@G y == F (x G y)  NB. Atop; Same as x ([: F G) y; Consider as golfing alternatives
x F&G y == (G x) F (G y)  NB. Compose; G is applied monadically to both arguments

一元动词

(F G H) y == (F y) G (H y)
(G H) y == y G (H y)  NB. Note that this is different from APL
([: G H) y == G (H y)
(F G H I J K) y == (F (G H (I J K))) y
                == y F ((G y) H ((I y) J (K y)))
F@G y == F (G y)

杂项

x&F y == x F y
F&y x == x F y
y F~ x == x F y
F~ y == y F y

技巧

(F x) G (H y)

隐性的解决方案:(G~F)~H; 根据实际动词,考虑重新排列左右参数以除去~

x ((G~F)~H) y
x (G~F)~ (H y)
(H y) (G~F) x
(H y) G~ (F x)
(F x) G (H y)

Monadic-Dyadic替换

>:y == 1+y
<:y == 1-~y or _1+y
+:y == 2*y
-.y == 1-y
-:y == 2%~y
*:y == 2^~y
#.y == 2#.y
#.inv y == 2#.inv y  NB. #: doesn't work this way
{.y == 0{y
{:y == _1{y
}.y == 1}.y
+/y == 1#.y

1
(G~F)~H是纯净的泡沫善良!
约拿

2

& 是你的朋友,明智地使用它

v是一个动词,n是名词,xy分别被左和右参数,。

Monad &:介绍~副词/连词内部

副词/连词链从左侧开始计算。所以像这样的东西是_2&+/\&.>行不通的,因为它像(_2&+)/\&.>我们想要的那样进行解析_2&(+/\)&.>。在这种情况下,交换的左/右+/\可以节省一个字节,+/\~&_2&.>因为这个解析为((+/\)~)&_2&.>。要了解为什么这样做:

+/\~&_2 y
is equivalent to
y +/\~ _2
is equivalent to
_2 +/\ y
is equivalent to
_2&(+/\) y

Dyad &:重复x次数

你知道吗,如果你给一个左参数x&,该功能适用于它xy?很多挑战要求您执行某些操作x时间。它主要可以通过两种方式实现:

  • 使用^:没有正确操作数的幂运算符

如果操作是v,则v^:成为副词串,当给定左操作数时,它成为一元动词。因此v适用于yx时间。

x(v^:)y
is equivalent to
(v^:x)y
  • 使用二进位&作为最外层连词

要使用此功能,您需要标识一个常数n和二元动词u,以使n u yy u n等效于v。然后,您可以编写n&uu&n解决整个任务。当常量的选择很明显时,例如3 in 3 u:(将字符转换为ASCII值),这种形式最有效。

另外,与的最外面的结构是连词或副词(在这种情况下应该是;可以代替)相比,u&n它稍微更可取。n&uun&un&(u)u~&n

请注意,您可以&在火车上的任何地方放置二进角,以实现将任意函数重复到任意参数,这与dynamic类似^:

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.