既然我已经完全沉迷于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会填写.
和(
; Uc
Japt 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
它在字符串上和在数组上一样有效,因此我们可以删除两个q
s:
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或123d
Japt中使用以查找适当的字符。
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 s16
6个字节-我赢了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}
→ mc
m@Xc1}
→ mc1
m@X+1}
→ m+1
m@1+X}
→ m!+1
m@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.d
Array.prototype.every
Array.e
对于字符串方法,最好知道在传递带有或不带有g
标志的字符串或正则表达式之间的行为有何不同。
String.prototype.match
String.f/o
String.prototype.search
String.â
String.prototype.replace
String.e/k/r
e/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
方法简化了数组,当在整数上使用该方法时,该数组将返回整数和方法的参数中的较大者(例如,2w5
return 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