既然我已经完全沉迷于Code Golf,那么现在大概是时候尝试一些高尔夫语言了。
鉴于我几乎只使用JavaScript进行游戏,因此Japt似乎是一门逻辑语言。我将在下一次获得的机会中深入阅读文档,但与此同时,请在下面的答案中发布您对Japt的任何建议。
因为我通常是Japt和高尔夫语言的初学者,所以如果可以的话,如果可以将技巧“翻译”为JavaScript,那将对我有所帮助有很大帮助。
既然我已经完全沉迷于Code Golf,那么现在大概是时候尝试一些高尔夫语言了。
鉴于我几乎只使用JavaScript进行游戏,因此Japt似乎是一门逻辑语言。我将在下一次获得的机会中深入阅读文档,但与此同时,请在下面的答案中发布您对Japt的任何建议。
因为我通常是Japt和高尔夫语言的初学者,所以如果可以的话,如果可以将技巧“翻译”为JavaScript,那将对我有所帮助有很大帮助。
Answers:
您可能知道,Japt只是JavaScript的缩短,扩展版本。我之所以创建Japt是因为我厌倦了冗长的属性名(例如String.fromCharCode(x)和Math.floor(x))以及执行诸如创建范围之类的乏味工作。这是从JavaScript转到Japt时需要了解的最低要求:
U,V,W,X,Y,和Z; 完整的数组存储在中N。最后一个表达式的结果将自动打印。a- z(和à- ÿ)。当您使用这些字母之一时,Japt会填写.和(; UcJapt U.c(中的JavaScript 等同于JavaScript中的JavaScript,根据类型的不同,它们可能表示ceil,charCodeAt或concat U。这就是Japt大部分力量的来源。您可以在Japt文档的“ _____函数”部分(位于解释器)下找到这些方法的完整列表。),并)代表))。这是因为当我第一次设计Japt时,我想保存尽可能多的字节,这就是我最初想到的方式。(尽管Us w n看起来确实比Us)w)n)IMHO好。)ABC{...},其中ABC可以是任何变量字符串。函数在大多数情况下都像在JS中那样工作,主要区别是最后一个表达式会自动返回(而不是必须使用return或花哨的ES6括号)。'表示单个字符字符串(即'a与相同"a"),并#采用下一个字符代码并成为该数字(#e与相同101)。$在翻译过程中,美元符号之间的任何内容都保持不变。for例如,您可以使用它来实现循环,因为Japt没有这些循环,但是我建议您使用其他方法(例如m在字符串和数组或o数字上)。"",0-9,(,+,=等-保持不变时transpiled(在大多数情况下,无论如何)。这就是编写基本Japt代码所需的全部知识。要在Japt中获得最大的高尔夫能力,需要更多的知识,但这可以在其他答案中找到。
这是一个基本示例。假设您要使用一串ASCII字符,并用其十六进制字符代码替换每个字符。您可以在JavaScript中执行以下操作:
U.split("").map(x=>x.charCodeAt(0).toString(16)).join("")
现在转换为Japt。.split("")JS中的代码与q""Japt中的代码相同,甚至更短,只是q。.join("")也只是q,区别在于对象是数组而不是字符串。.map(是m,.charCodeAt(是c,.toString(是s。因此,我们的Japt代码可能类似于:
Uq mX{Xc0 s16} q
但是,在Japt中,m它在字符串上和在数组上一样有效,因此我们可以删除两个qs:
UmX{Xc0 s16}
在线测试!如您在“ JS代码”框中所看到的,它直接转换为:
U.m(function(X){return X.c(0).s(16)})
当您学习使用Japt时,您将越来越少地专注于从JavaScript来回转换,并能够以Japt作为其自己的语言进行编码。以下是完全省略JavaScript部分的说明:
UmX{Xc0 s16}
// Implicit: U = input string
UmX{ } // Take U, and replace each character X with the result of this function:
Xc0 // Take the char-code at index 0 in X (the first and only one).
s16 // Convert this to a hexadecimal string.
// Implicit: output result of last expression
Um_c s16?
¡Xc s16?
更新:此技巧中介绍的工具此后已被重写,改进并集成到我的Japt解释器中。为了获得最佳效果,建议您在以下任何链接的压缩机上使用该压缩机。当我有更多时间并重新考虑新的压缩器时,将重新讨论该技巧。
如果您的代码中有字符串数组,最明显的压缩方法是逐个运行每个字符串Oc。出于本技巧的目的,我们将使用array ["lollipop","marshmallow","nougat","oreo"],该数组最初的大小为42个字节。通过运行每个字符串可以Oc给我们:
[`lo¥ipop`,`Ú\hÚaow`,`Í`,`eo`]
现在是33个字节,节省了不少。
但是,我们可以做得更好。如果将数组连接到以换行符分隔的字符串,则可以除去方括号,逗号和多余的反引号,然后在换行符上拆分以获取数组。将其应用于示例数组可得到以下内容:
`lo¥ipop
Ú\hÚaow
Í
eo`·
现在减少到26个字节。
但是,我们仍然可以做得更好!我们可以使用小写字母来分隔字符串,而不是用换行符分隔,换行符可能包含在压缩中。z没有在我们的任何字符串中使用,所以让我们放下它,然后看看我们如何进行。
`lo¥ipopzÚ\hÚaowzÍzeo`qz
嗯,疯了-那里没有改善;我们的字节数增加了一个!有可能是另一封信中,你可以使用,但取决于你的字符串,可能有相当多的尝试-在我们的例子中有11: b,c,d,f,j,k,q,v,x,y,z。尝试每种方法都非常繁琐,这是此便捷工具的用武之地。给它输入换行符分隔的字符串,它将尝试使用每个字符串中未包含的每个字母来分隔字符串并输出:
通过运行我们的示例字符串显示出b最佳结果:
`lo¥ipáæqrÚaowbÍÞo`qb
在这里,我们只有24个字节。
但是,我们可以做的,甚至更好!如果数组中字符串的顺序无关紧要,则可能存在不同的排列组合和不同的定界符,甚至可能更短。但是,尝试每种可能性将变得更加乏味。使用我们的4个字符串,可以尝试24种不同的排列。11个可能的字母中的每一个都变成264!这就是该工具发挥作用的地方。同样,用换行符分隔的字符串输入它,它将尝试每个排列和每个定界字母的每种组合,输出:
通过我们的示例字符串运行表明,"nougat","oreo","lollipop","marshmallow"使用b作为定界符可提供最佳结果,最终字节数仅为23:
`ÍÞo½o¥ipáæqrÚaow`qb
您可以将相同的原理应用于整数数组,方法是先将整数转换为更高的基数。使用此示例,36字节数组:
[588181,156859,595676,475330,680474]
通过首先将其转换为基本的32个字符串数组,然后通过第一个压缩程序运行它,我们可以将其减少到29个字节:
`huclt4p5r5ÛÊg62tkogq`qt mnH
或使用第二个程序低至27个字节:
`4p5Ïcl5ÛÊg62tkogq`qt mnH
通过将整数转换移动到您已经在数组上运行的方法中,您也许可以在其上另外节省一个字节或2个字节。
q<letter>(<space>)费用计算在内·。虽然,您可以使用Unicode快捷方式之一取回字节,具体取决于您的分隔符(例如,qÊ与相同ql<space>)。Japt(当前)使用shoco库进行字符串压缩。您可以使用压缩任意字符串Oc,只要它包含小写字母即可:
Oc"Hello, World!"
输出 HÁM, WŽld!(嗯,从Ž技术上讲是不可打印的字符)。您可以通过将其包装在反引号中来对其进行解压缩:
`HÁM, WŽld!`
或者,您可以使用该Od函数解压缩任意字符串。这通常没有用,但有其用途 ...
HÁM, WŽld!还是将其括在反引号中?我猜是后者。
在Japt中,可以使用#,后跟一个字符来创建字符代码。当缩短更长的数字时,这非常方便。
如@ETHproductions所述,这仅适用于100-255范围内的三位数运行,除非您愿意切换到UTF-8。
123 可以缩短为 #{
101 可以缩短为 #e
您甚至可以将它们链接在一起:
123101 可以缩短为 #{#e
您可以String.fromCharCode(123)在JavaScript或123dJapt中使用以查找适当的字符。
String.fromCharCode(123) 退货 {
String.fromCharCode()是很长的JS方法之一,可以节省您的字节数。大概这些会被认为是整数吗?即,如果我123在解决方案中需要整数,则可以#{用来保存一个字节。
String.fromCharCode(123)在JavaScript的作品,但你可以做123d的Japt得到相同的结果;-)此外,这只能在三位数的运行范围内100- 255(除非你愿意切换到UTF-8)
[]Japt有一个用于空数组的常量:A。但是,为了访问它,您必须;在程序前添加分号以使用Japt的替代常量,否则A为10。因此,使用;A实际上提供了一个0字节节省了[],但会保存,如果您需要将数组赋值给一个变量(例如,您字节A=[])。
但是,如果(且仅当)程序不接收任何输入,则可以使用N变量(即输入数组)访问只有1个字节的空数组-没有输入,它将为空。在这里尝试。
这还有一个额外的好处,即允许您在程序中使用默认常量值,并且在某些情况下,;A即使您的程序正在输入内容,也仍然可以通过使用s1和的快捷键来节省字节数s2。
N,好主意。
Japt允许您通过环绕原始JavaScript来执行原始JavaScript $...$。
例如, $alert("hello world")$
可以通过利用Japt的自动关闭功能来缩短 $和)。
$alert("hello world")$ 可以缩短为 $alert("hello world"
您还可以使用压缩JavaScript Ox。
如果要使用一个JavaScript函数,例如screen.width,可以"screen.width"使用Oc压缩字符串,然后将结果在Ox`之间...`
请注意,当Japt中没有其他任何后缀时,您不需要在Japt中使用右引号。
根据最新的元共识(2017年12月),命令行标志不再计入字节。对于Japt而言,这确实是一个好消息,因为它有许多标志可以对输入/输出进行额外处理。
Japt中所有可用的标志按评估顺序在下面进行描述。同一组中的标志互斥。请注意,在不同群体的标志可以组合使用,导致像这样 :)
mdefæ整个程序映射到第一个参数(U)上。
如果存在更多参数,则按原样传递它们(即,不成对映射)。否则,第二个参数是索引,第三个参数是整个数组,就像U.m。如果U是数字,则转换为范围;如果是字符串,则将其转换为字符数组,并将结果连接在一起。
-m:适用以上条件,别无其他。-d:true如果某些结果为真,false则返回,否则返回。-e:true如果所有结果均为真,false则返回,否则返回。-f:返回U结果为真的元素数组。-æ:应用-f并返回其第一个元素。gh采用指定索引处的元素。
-g:采用第一个元素(索引0)。-gX:采用索引处的元素X(可以是任何正整数)。-h:采用最后一个元素。!¡将结果转换为布尔值。
-!:不应用布尔值。-¡:不两次使用布尔值(返回真实性)。N将结果转换为数字。使用一元加号。
PRSQ转换为某种字符串。
-P:用联接数组""。-R:用联接数组"\n"。-S:用联接数组" "。-Q:应用JSON.stringify(可以是任何对象,而不仅仅是数组)。实例。x将功能x应用于输出。(从字面上看x,不是“任何单个小写字母函数”。)
有在Japt许多共同的结构,只是不能被存储在一个单一的ASCII字符,例如qS ,p2 ,mX{,} ,等于是来解决这个问题,Japt有“的Unicode快捷方式”,这是在范围内的字符\xA1- \xDE(¡- Þ)扩展到这些常见结构。您可以在解释器docs中找到这些的完整列表。
另外, @代表XYZ{,和_代表Z{Z,以帮助构建功能。因此,让我们从另一个答案了解我们的示例程序:
UmX{Xc0 s16}
首先,我们可以将替换X{X为_,从而得到:
Um_c0 s16}
然后我们可以替换m_为®保存另一个字节:
U®c0 s16}
或者我们可以替换 X{为@,从而得到:
Um@Xc0 s16}
然后,这使我们可以使用¡快捷方式保存两个字节:
¡Xc0 s16}
这两个路径之一可以比另一个缩短1个字节。你能找出哪个吗?
®c s166个字节-我赢了cookie吗?!
®c sG吗?
csG。
变量A- S预设为公共值,这些公共值用一个以上的字节表示在Japt中:
A- G是10- 16。H是32,I是64,J是-1,L是100。K被定义为new Date(),您可以通过多种方式进行操作。M并且O是具有各种有用的功能的对象。您可以在文档中了解更多信息。P是空字符串,Q是引号,R是换行符,并且S是空格。T 设定为 0,因此如有必要,您可以将其用作累加器。如果程序中的第一个字符是分号;,A-L则按以下方式重置:
A 是空数组 []。B 是 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"。C 是 "abcdefghijklmnopqrstuvwxyz"。D 是 "QWERTYUIOP\nASDFGHJKL\nZXCVBNM"。E是"[a-z]",并且F是"[A-Za-z]"(在我将它们添加为正则表达式功能之前很有用)G是36,H是65和I是91(适用于字母范围)。J是一个逗号;L,单期。如今只A,B,C,并D从该列表是非常有用的。我打算添加一个更好的系统,该系统最多允许256个2字节的变量,这些变量将被预置为这些值以及更多。
你很可能已经知道,@并且_是快捷键XYZ{和Z{Z,分别为(涵盖了Unicode的快捷键的答案)。但是有时您可以使功能更短。
假设您有一个字符数组,并且想将每个字符映射到其字符代码。您可以使用以下任一方法执行此操作:
mX{Xc}
m_c}
但是有更好的方法。如果方法或运算符是另一个方法或a之后的第一项(,则它将变成字符串。因此,这两行是等效的:
r'a'b // Replace all "a"s with "b"s; transpiles to .r("a","b")
ra'b // Does the same thing, 1 byte less; transpiles to the same thing
但这对我们的功能有何帮助?好吧,大多数接受函数的方法(如果给出了表示方法或运算符的字符串)都将其解释为函数。这意味着您也可以执行以下操作:
m_c} // Map each item to its char code
m'c // Does the same thing, 1 byte less
mc // Also does the same thing, 2 bytes less
我称这些为“自动功能”。有几种不同的品种:
m@Xc} → mcm@Xc1} → mc1m@X+1} → m+1m@1+X} → m!+1m@2pX} → m!p2希望你能明白。要交换参数,只需在方法或运算符前加上!。
m@2pXÃ→交通m!p2<space>→交通m!²。
每当您在Japt中开始新行时,前一行的结果都会自动分配给输入变量(U- Z)中的一个U,第一行为,第二行为V依此类推。
让我们举个例子:假设您要创建2个要使用的数组,一个数组包含数字1-10,另一个数组包含其正方形。很长的路要这样:
U=Aõ V=Um² [do something with the arrays]
但是,使用自动变量分配可以将其简化为:
Aõ
Um²
[do something with the arrays]
我们在那里保存了4个字节。但是,在这种情况下,我们可以再节省一个字节,因为将1-10的数组分配给了它,U并且U在某些情况下可以省略:
Aõ
m²
[do something with the arrays]
需要注意的一件事是,您不会覆盖以后在程序中可能需要使用的任何输入变量。可以通过在开头保留一个或多个空行来避免这种情况。在下面的示例中,将2个数组分配给变量V&W,而不是U&V:
Aõ
Vm²
[do something with the arrays]
由于任何Japt代码都可以作为已编译的JS运行,因此,对JS运算符和内置方法的深入了解将对Japt代码的开发有所帮助。
U,然后求值V.m。(具有换行符会U在第一行激活隐式赋值。)[]Vm@...
...
仔细查看将哪些参数传递给函数参数。
Array.prototype.map
Array.a/b/m/x/y/í,Number.o/õ,String.m/y/íArray.prototype.reduce
Array.r/å,String.å,对于å,第四个参数未通过。Array.prototype.some
Array.dArray.prototype.every
Array.e对于字符串方法,最好知道在传递带有或不带有g标志的字符串或正则表达式之间的行为有何不同。
String.prototype.match
String.f/oString.prototype.search
String.âString.prototype.replace
String.e/k/re/r,可以将函数作为第二个参数传递,并且强烈建议您理解函数参数。对于大多数不太困难的挑战,您可以只用一行Japt来表达解决方案,这是一系列应用内置函数的过程。但是,更复杂的代码将需要使用循环构造,递归或重用大量代码。这就是多行编程的用武之地。
任务:给定一个数字数组,将每个元素与索引平方成对,然后按总和对其进行排序。
[5,1,17,9,3] => [[5,0],[1,1],[17,4],[9,9],[3,16]] => [[1,1],[5,0],[9,9],[3,16],[17,4]]
单行解决方案是íUm@Yp2})ñx,但是})花费两个字节(并且没有一字节的快捷方式)。您可以})通过将尾随的内容移动ñx到下一行来删除它,因此代码如下所示:
íUm@Yp2
ñx
编译的JS变为:
U = U.í(U.m(function(X, Y, Z) { return Y.p(2) })); U.ñ("x")
您可以清楚地看到这与单行解决方案的作用相同,只是将中间结果分配回U。
如果未指定,则递归函数ß将全部UVWXYZ作为隐式参数。U显然是主要输入,但是您可以使用其中任何一个VWXYZ来跟踪所需的其他值。例如,您可以执行以下操作:
(modify input and implicit assign to U)
(modify V and implicit assign to V)
(test something and call ß without arguments; U and V are passed automatically)
另外,如果您想要的只是一个临时变量,则可以使用内联赋值(例如(T=...))作为变量T很少会原样使用(0)。
为此,我认为我无法提出出色的示例任务,因此,我将参考使用本技巧的唯一解决方案,并概述一些一般性想法。
{,@或_执行此工作。另外,您也可以做一些类似的事情(T=@...}),将功能分配嵌入到更复杂的行中。V是一个函数,我们想V(U)在JS中调用。VU不起作用,因为它只是意味着V,U。V(U也没有 是V,(U)。甚至函数方法也没有太大帮助。我们发现的最好方法是:
[U]xV (映射和总和),如果结果是数字UmV如果U是单个字符并V返回字符串,则$V($U或[U]mV g一般而言。UmV。要查找第一个满足的整数V,请使用Va。作为ETH关于自动功能的一般技巧的后续,该技巧将提供一些具体的字节保存技巧的示例,您可以使用这些技巧来实现,我将在以后的文章中补充这些技巧。
假设我们已将数组[3,1,4,2]分配给变量,U并且我们希望从中获取最大的数字。我们可以对数组进行排序,然后弹出最后一个元素,以4个字节为单位:
Un o
不利的是我们已经修改了原始数组。U现在[1,2,3]可能并不总是理想的。幸运的是,有一种方法可以在不修改也缩短一个字节的数组的情况下进行操作:
Urw
我们在此所做的工作是使用w方法简化了数组,当在整数上使用该方法时,该数组将返回整数和方法的参数中的较大者(例如,2w5return 5)。因此,以上等效于UrÈwY或UrXY{XwY}。但是请注意,在数组中所有整数均为负的情况下,此技巧不起作用。
íí是一个有用的内置zip函数,它对(或多个)两个数组或字符串进行配对,并可以选择通过函数映射每对。但是,当给定不均匀的数组或字符串时,当前存在一些小问题:
undefined。例如,这可能很难比较两个不均匀的字符串,并从每对中获取具有较高代码点的char。即使您知道那U将是更长的时间,解决这个简单的任务仍然需要很多字节:
UíUç hV @[XY]n o
相反,您可以做的是将输入作为由两个字符串组成的数组,使用进行转置y,然后将每行映射到正确的结果:
Uy m_q n o
这具有始终在较短的字符串上填充空格的优点,这使整个两个字符串都变得很容易。
更新:Japt现在具有ASCII范围的常量;的替代值
E,可通过访问;。有关Japt常数的更多信息,请参见本技巧。
尽管Japt还没有ASCII范围的内置函数,但是您可以仅用5个字节生成一个字符数组:
95odH
95o[0,95)通过自动功能 传递每个元素来创建范围,该功能d在用于数字时将返回该代码点处的字符。d在这种情况下,将数字作为参数传递给方法H为32的Japt常数,它将在转换前添加到原始数字中。
JavaScript中的等效解决方案是:
[...Array(95)].map((_,x)=>String.fromCharCode(x+32))
要获取ASCII范围内的随机字符,请ö改用,它从range [0,X)处返回一个随机数,在此范围内X将运行该数字。
95ö dH
或者,要获取包含多个随机字符的数组,请将所需的字符数作为参数传递ö。以下将返回10个字符:
95öA mdH
通过结构字符,我的意思是{},(),$,甚至"和`。通常,只要这些字符出现在程序结尾(例如UmX{Xc +"; "} -> UmX{Xc +"; ),通常都可以将其删除。
此外,只要它们出现在以下位置,您就可以删除括号或空格:
;(或程序结尾)为准;{(并且通过扩展,@)或[,或左]或}。另外,很少需要用逗号分隔参数。如果你写AB,例如,Japt知道你的意思A,并B分别。您只需要用逗号分隔两个数字文字,例如Us2,5。
最后,如果有一个U在节目开始或之后{或者;,随后的方法调用(小写字母或相关的Unicode快捷方式)或不含任何二进制运算符+和-(*,&,==等),你可以删除U保存字节,Japt会为您插入。
U即使不是在程序开始时也可以忽略。
{或之后使用;。您还有其他人知道吗?(自从我编写了此功能以来已经有一段时间了:P)
有时您可能需要修改数组中的最后一个元素,因此,这里有一个简短的说明。我们将使用[2,4,8,32]分配给输入变量的数组U并将最后一个整数除以(32)除以2。
实现此目标的明显方法是使用以下9字节解决方案(Demo):
UhJUgJ /2
hnx将索引处的元素设置n为x。gn 返回索引处的元素 n。J 是Japt常数 -1,这要归功于Japt对负索引的支持,它使我们能够处理数组中的最后一个元素;如果您不知道数组的大小,则非常方便。/2简单地除以2。因此上述套在索引元件-1的阵列的元素索引在-1所述阵列由2或者在JavaScript除以:U[3]=U[3]/2。当您以这种方式写出来时,这似乎太漫长了。幸运的是,有是一个较短的方式; 我们可以从数组中弹出最后一个元素,对其进行修改并将其推回到数组中。单独执行每个操作将占用9个以上的字节,但是我们可以一次仅用7个字节就完成所有操作,节省了2个字节(Demo)
UpUo /2
翻译成JS,相当于:
U.push(U.pop()/2)&&U