ECMAScript 6 이상에서의 골프 팁


88

이것은 다른 "<...>에서의 골프 팁"과 유사하지만 특히 ECMAScript 6 이상에서 제공되는 JavaScript의 새로운 기능을 대상으로합니다.

JavaScript는 본질적으로 매우 장황한 언어입니다. function(){}, .forEach()문자열을 배열로 변환, 배열 같은 객체를 배열로 변환하는 등은 부풀려서 골프에 적합하지 않습니다.

반면 ES6 +에는 매우 편리한 기능과 설치 공간이 줄었습니다. x=>y, [...x], 등 그냥 예 중 일부입니다.

코드에서 여분의 몇 바이트를 제거하는 데 도움이되는 몇 가지 유용한 트릭을 게시하십시오.

참고 : ES5에 대한 트릭 은 JavaScript의 골프 팁 에서 이미 사용 가능합니다 . 이 스레드에 대한 답변은 ES6 및 기타 향후 ES 버전에서만 사용 가능한 트릭에 중점을 두어야합니다.

그러나이 스레드는 현재 ES5 기능을 사용하여 골프를 타는 사용자를위한 것입니다. 답변에는 ES6 기능을 이해하고 자신의 ES5 코딩 스타일에 매핑하는 데 도움이되는 팁도 포함될 수 있습니다.

답변:


42

스프레드 연산자 ...

스프레드 연산자는 배열 값을 쉼표로 구분 된 목록으로 변환합니다.

사용 사례 1 :

함수가 목록을 기대하는 배열을 직접 사용하십시오.

list=[1,2,3]
x=Math.min(...list)
list=[10,20], a.push(...list) // similar to concat()

사용 사례 2 :

iterable (일반적으로 문자열)에서 배열 리터럴을 만듭니다.

[...'buzzfizz'] // -> same as .split('')

사용 사례 3 :

함수에 대한 가변 개수의 인수를 선언

F=(...x) => x.map(v => v+1)
// example: F(1,2,3) == [2,3,4]

모질라 문서보기


3
이제 여기에 공감대가 있습니다. 분명히 누군가가이 팁에서 끔찍한 것을 지적했으며, 너무 부끄러워서 의견을 남기고 설명 할 수
없었습니다

괜찮아 보인다. 세미콜론이 부족했을까요? ;) (btw, 루비의 표시와 같은 휴식 매개 변수로 사용할 수도 있습니다)
gcampbell

함수 시그니처에도 유스 케이스가 있다고 덧붙일 수 있습니다. :
Felix Dombek

Misclick은 하향 투표를 의미하지 않았습니다
Stan Strum

@StanStrum 발생합니다. 이 게시물을 약간 업데이트하여 결국 투표를 변경할 수 있습니다 (또는 이미 했습니까?)
edc65

21

내가 가입 한 이후로 트릭을 배웠다

내 주요 프로그래밍 언어는 JS이며 대부분 ES6입니다. 일주일 전이 사이트에 가입 한 이후, 동료 회원들로부터 많은 유용한 트릭을 배웠습니다. 나는 여기에 그 중 일부를 결합하고 있습니다. 커뮤니티에 대한 모든 크레딧.

화살표 함수 및 루프

우리는 화살표 기능이 많은 바이트를 절약한다는 것을 알고 있습니다.

function A(){do something} // from this
A=a=>do something // to this

하지만 몇 가지를 명심해야합니다

  • 사용 클럽 여러 제표에 대한 시도 ,즉, (a=b,a.map(d))반환되는 값이 마지막 표현이다, 여기에서 -a.map(d)
  • 귀하의 경우 do something부분은 하나 이상의 문입니다, 당신은 주변에 추가해야 {}브래킷.
  • {}대괄호 가 있으면 명시적인 return 문을 추가해야합니다.

위의 내용은 루프가 관련된 많은 경우에 적용됩니다. 그래서 같은 :

u=n=>{for(s=[,1,1],r=[i=1,l=2];c=l<n;i+=!c?s[r[l++]=i]=1:1)for(j of r)c-=j<i/2&s[i-j];return n>1?r:[1]}

여기에서 반환으로 인해 9 자 이상을 낭비하고 있습니다. 이것은 최적화 될 수 있습니다.

  • for 루프를 피하십시오. 사용 .map하거나 .every또는 .some대신. 매핑하는 것과 동일한 어레이를 변경하려는 경우 실패합니다.
  • 루프를 폐쇄 화살표 함수로 감싸서 기본 화살표 함수를 단일 명령문으로 변환하십시오.

위의 내용은 다음과 같습니다.

u=n=>(s=>{for(r=[i=1,l=2];c=l<n;i+=!c?s[r[l++]=i]=1:1)for(j of r)c-=j<i/2&s[i-j]})([,1,1])|n>1?r:[1]

제거 된 문자 : {}return

추가 된 문자 : (){}>|

변수를 올바르게 채우는 클로저 메소드를 호출 n한 다음 클로저 메소드가 아무것도 리턴하지 않기 때문에 (즉, 리턴 undefined), 비트 단위 또는 n외부 화살표 함수의 단일 명령문으로 배열을 모두 리턴합니다.u

쉼표와 세미콜론

지금까지의 것을 피하십시오.

루프에서 변수를 선언하거나 이전 섹션에서 언급 한 것과 같이 ,분리 된 명령문을 사용하여 단일 명령문 화살표 기능을 사용하는 경우,이를 피 ,하거나 ;마지막 몇 바이트를 제거 하기 위해 약간의 멋진 트릭을 사용할 수 있습니다 .

이 코드를 고려하십시오.

r=v=>Math.random()*100|0;n=r();m=r();D=v=>A(n-x)+A(m-y);d=0;do{g();l=d;d=D();....

여기서는 많은 변수를 초기화하기 위해 많은 메소드를 호출하고 있습니다. 각 초기화는 ,또는을 사용하고 ;있습니다. 이것은 다음과 같이 다시 작성할 수 있습니다.

r=v=>Math.random()*100|0;n=r(m=r(d=0));D=v=>A(n-x)+A(m-y);do{d=D(l=d,g());....

메소드가 전달 된 변수를 방해하지 않는다는 사실을 사용하고 그 사실을 사용하여 3 바이트를 면도하는 방법에 주목하십시오.

기타

.search 대신에 .indexOf

둘 다 동일한 결과를 제공하지만 search더 짧습니다. 검색에는 정규 표현식이 필요하지만 현명하게 사용하십시오.

`템플릿 문자열`

특정 조건에 따라 하나 이상의 스트링 부품을 연결해야 할 때 매우 유용합니다.

JS에서 quine을 출력하려면 다음 예제를 사용하십시오.

(f=x=>alert("(f="+f+")()"))()

vs.

(f=x=>alert(`(f=${f})()`))()

두 개의 큰 따옴표 (`) 안에있는 문자열 인 템플릿 문자열에서 a 안에있는 모든 것을 ${ }코드로 취급하고 결과 답변을 문자열에 삽입합니다.

나중에 몇 가지 요령을 게시하겠습니다. 행복한 골프!


1
. 검색이 더 짧습니다. 가능하면 사용하십시오! 그러나 .indexOf와 동일하지 않습니다. .search는 regexp문자열이 아닌을 원합니다 . 시도'abc'.search('.')
edc65

@ edc65 업데이트!
Optimizer

인스턴스 메소드를 사용하여 원래 배열을 수정할 수 있습니다. 두 번째는 현재 인덱스이고 세 번째는 반복되는 배열입니다.
Isiah Meadows

8
"일주일 전 사이트에 가입"
-21.4k

2
뿐만 아니라 .map재귀는 for루프를 표현식으로 바꾸는 데 도움이되는 또 다른 기술입니다 .
Neil

20

속성 속기 사용

속기 속성을 사용하면 변수를 배열 값으로 설정할 수 있습니다.

a=r[0];b=r[1] // ES5
[a,b]=r       // ES6 - 6 bytes saved

이것은 또한 다음과 같이 사용될 수 있습니다 :

a=r[0],b=r[2] // ES5
[a,,b]=r      // ES6 - 5 bytes saved

이것을 사용하여 변수를 뒤집을 수도 있습니다.

c=a,a=b,b=c // ES5 - uses extra variable
[b,a]=[a,b] // ES6 - not shorter, but more flexible

slice()기능을 사용하여 기능 을 단축 할 수도 있습니다 .

z = [1, 2, 3, 4, 5];

a=z.slice(1) // a = [2,3,4,5]; ES5
[,...a]=z    // a = [2,3,4,5]; ES6

기본 전환

ES6은 Base-2 (이진) 및 Base-8 (8 진수) 형식을 10 진수로 변환하는 훨씬 짧은 방법을 제공합니다.

0b111110111 // == 503
0o767       // == 503

+이진수, 8 진수 또는 16 진수 문자열을 10 진수로 변환하는 데 사용할 수 있습니다. 당신은 사용할 수 있습니다 0b, 0o그리고 0x각각 진수, 8 진수와 진수를 들면 :

parseInt(v,2) // ES5
+('0b'+v)     // ES6 - 4 bytes saved; use '0o' for octal and '0x' for hex
'0b'+v-0      // Shorter, but may not work in all cases
              // You can adapt this your case for better results

이> 7 번을 사용하는 경우 사용 parseInt하고 이름을 바꾸는 것이 더 짧 습니다.

(p=parseInt)(v,2)

이제에 p사용할 수있어 parseInt장기적으로 많은 바이트를 절약 할 수 있습니다 .


기본 변환 트릭은 좋지만 변환 수는 리터럴 대신 변수 형태 일 가능성이 높으며이 경우 훨씬 길어집니다.
Optimizer

1
'0x'+v-0더 짧지 만 일부 시나리오에서는 제대로 작동하지 않을 수 있습니다.
ETHproductions

1
그런데, 0767(ES5)는 0o767(ES6) 표기법 보다 짧습니다 .
Camilo Martin

@CamiloMartin 0767은 비표준 확장이며 엄격 모드에서는 명시 적으로 금지되어 있습니다.
Oriol

1
@Oriol 엄격 모드는 나쁜 밈이었습니다. 성능에 도움이되지 않았고 실제로 좋은 코드를 작성하도록 강요하지 않았으며 어쨌든 기본값이되지는 않았습니다. 0접두사 8 진 리터럴은 아무데도 가지 않으며 유효한 ecmascript 0o입니다.
Camilo Martin

19

함수와 함께 문자열 템플릿 사용

하나의 문자열을 인수로 사용하는 함수가있는 경우 ()식이없는 경우를 생략 할 수 있습니다 .

join`` // Works
join`foobar` // Works
join`${5}` // Doesn't work 

9
경고, 이것은 실제로 배열을 전달합니다. fun`string` 와 같지 fun(["string"])않습니다 fun("string"). 이것은 문자열처럼 캐스트되는 함수 alert에는 적합하지만 다른 경우에는 문제가 발생할 수 있습니다. 자세한 내용은 MDN 기사
Cyoce

5
빠른 참고 사항 : fun`foo${1}bar${2}baz전화와 동일fun(["foo","bar","baz"],1,2)
Cyoce

14

배열 이해 (Firefox 30-57)

참고 : 배열 이해는 표준화되지 않았으며 Firefox 58에서는 더 이상 사용되지 않습니다.


원래 ECMAScript 7 사양에는 여러 가지 새로운 배열 기반 기능이 포함되었습니다. 이들의 대부분이 확정 버전으로하지 않았지만, 파이어 폭스 지원 (ED)이 기능의 가능성이 가장 큰 : 대체 할 수있는 멋진 새 구문 .filter.mapfor(a of b)구문. 예를 들면 다음과 같습니다.

b.filter(a=>/\s/.test(a)).map(a=>a.length)
[for(a of b)if(/\s/.test(a))a.length]

보시다시피, 두 줄은 부피가 큰 키워드와 화살표 기능을 포함하지 않는 두 번째 줄 외에 다른 것이 아닙니다. 그러나 이것은 주문만을 설명한다 .filter().map(). .map().filter()대신에 어떻게됩니까 ? 실제로 상황에 따라 다릅니다.

b.map(a=>a[0]).filter(a=>a<'['&&a>'@')
[for(a of b)if(a<'['&&a>'@')a[0]]

b.map(a=>c.indexOf(a)).filter(a=>a>-1)
[for(a of b)if((d=c.indexOf(a))>-1)d]

b.map(a=>a.toString(2)).filter(a=>/01/.test(a))
[for(a of b)if(/01/.test(c=a.toString(2)))c]

또는 당신이 무엇을 원하는 경우 중 하나 .map 또는 .filter ? 글쎄, 그것은 일반적으로 덜 OK로 나타납니다.

b.map(a=>a.toString(2))
[for(a of b)a.toString(2)]

b.filter(a=>a%3&&a%5)
[for(a of b)if(a%3&&a%5)a]

따라서 제 충고는 일반적으로 .map and를 사용하는 곳마다 배열 이해를 사용하는 .filter것이지만 둘 중 하나만 사용하는 것이 아닙니다.

문자열 이해

ES7 이해에 대한 좋은 점은 .mapand와 같은 배열 별 함수와 달리 배열 뿐만 아니라 반복 가능한 객체 에서도.filter 사용할 수 있다는 것 입니다. 문자열을 다룰 때 특히 유용합니다. 예를 들어 문자열을 통해 각 문자를 실행하려면 다음을 수행하십시오 .cc.charCodeAt()

x=>[...x].map(c=>c.charCodeAt())
x=>[for(c of x)c.charCodeAt()]

그것은 상당히 작은 규모로 절약 된 2 바이트입니다. 문자열에서 특정 문자를 필터링하려면 어떻게해야합니까? 예를 들어, 이것은 대문자 만 유지합니다 :

x=>[...x].filter(c=>c<'['&&c>'@')
x=>[for(c of x)if(c<'['&&c>'@')c]

흠, 그것은 더 짧지 않습니다. 그러나 우리가 두 가지를 결합하면 :

x=>[...x].filter(c=>c<'['&&c>'@').map(c=>c.charCodeAt())
x=>[for(c of x)if(c<'['&&c>'@')c.charCodeAt()]

와우, 전체 10 바이트가 절약되었습니다!

문자열 이해의 또 다른 장점은 하드 코딩 된 문자열이 추가 바이트를 절약한다는 것입니다 of.

x=>[...'[](){}<>'].map(c=>x.split(c).length-1)
x=>[for(c of'[](){}<>')x.split(c).length-1]

x=>[...'[](){}<>'].filter(c=>x.split(c).length>3)
x=>[for(c of'[](){}<>')if(x.split(c).length>3)c]

인덱싱

배열 이해는 문자열 / 배열에서 현재 색인을 얻는 것이 조금 더 어려워 지지만 수행 할 수 있습니다.

a.map((x,i)=>x+i).filter ((x,i)=>~i%2)
[for(x of(i=0,a))if(++i%2)x+i-1]

주의해야 할 것은 조건이 충족 될 때뿐만 아니라 매번 인덱스가 증가 하도록하는 것입니다.

발전기 이해

생성기 이해는 기본적으로 배열 이해와 동일한 구문을 갖습니다. 괄호를 괄호로 바꾸십시오.

x=>(for(c of x)if(c<'['&&c>'@')c.charCodeAt())

이것은 배열과 거의 같은 방식으로 작동 하는 생성기 를 생성 하지만 다른 대답에 대한 이야기입니다.

요약

기본적으로 이해력은 일반적으로보다 짧지 만 .map().filter()상황의 세부 사항에 따라 결정됩니다. 두 가지 방법으로 시도하고 어느 것이 더 잘 작동하는지 확인하는 것이 가장 좋습니다.

추신 : 다른 이해 관련 팁이나이 답변을 향상시킬 수있는 방법을 제안하십시오!


다음은 몇 가지 캐릭터를 더 절약 할 수있는 범위에 대한 요령입니다.(x,y)=>[...Array(y-x)].map(a=>x++)
Mwr247

2
0에서 x 사이의 범위를 만들기 위해 11 바이트를 더 잘라낼 수 있습니다.x=>[...Array(x).keys()]
Mwr247

이해를위한 마지막 하나 : n=>[for(x of Array(n).keys())if(/1/.test(x))x](7 바이트 절약)
Mwr247

@ Mwr247 사실, 지금은 범위가 다른 훌륭한 ES6 기능만큼 이해력이 부족하지 않다는 것을 알 수 있습니다. 대신 문자열에 섹션을 추가하고 범위를 처리하겠습니다.
ETHproductions

Array Comprehensions는 더 이상 사용되지 않으며 모든 최신 버전의 Javascript에서 제거되었습니다. 주제에 대한 MDN 문서 를 참조하십시오 .
키퍼 루크

13

ES6의 함수 표현식은 화살표 표기법을 사용하며 ES5 버전과 비교할 때 바이트를 많이 절약하는 데 도움이됩니다.

f=function(x,y){return x+y}
f=(x,y)=>x+y

함수에 하나의 매개 변수 만있는 경우 괄호를 생략하여 2 바이트를 저장할 수 있습니다.

f=x=>x+1

함수에 매개 변수가 없다면 1 바이트를 저장하는 것처럼 선언하십시오.

f=()=>"something"
f=x=>"something"

주의 : 화살표 기능은 정확히 동일하지 않습니다 function () {}. 규칙 this이 다릅니다 (더 나은 IMO). 문서 보기


2
그러나 골프를 타는 경우 일반적으로 this등을 신경 쓰지 않습니다 .
Optimizer

1
일반적으로 그렇지는 않지만주의 할 점은 알 수없는 경고입니다. 람다는 프로덕션에서이 바인딩이 로컬에서 필요하지 않은 것이 더 일반적입니다.
Isiah Meadows

당신이 당신의 모든 인수를 취하려는 경우 또한, 당신은 "나머지"인수 기능, 예를 사용할 수 있습니다, f=(...x)=>x 이있을 것입니다 f(1,2,3) => [1,2,3].
코너 오브라이언

1
다음은이 사이트에만 적용되는 팁입니다. 양식 (x,y)=>...을 사용하는 함수를 사용하여 응답 하면 바이트를 카레 로 바꾸면됩니다.x=>y=>...
Cyoce

12

eval여러 명령문과 함께 화살표 함수에 사용return

내가 우연히 발견 한 더 우스운 트릭 중 하나 ...

여러 개의 문과을 필요로하는 간단한 화살표 함수를 상상해보십시오 return.

a=>{for(o="",i=0;i<a;i++)o+=i;return o}

에있는 a모든 정수를 반복 하는 단일 매개 변수를 허용하는 간단한 함수 [0, a)로, 출력 문자열의 끝 부분에 o이를 반환합니다. 예를 들어,이를 4매개 변수로 사용하여 호출하면 yield가 0123됩니다.

이 화살표 함수는 중괄호로 묶어야 하고 끝에 {}있어야합니다 return o.

이 첫 번째 시도의 무게는 39 바이트 입니다.

나쁘지는 않지만을 사용 eval하면이를 개선 할 수 있습니다.

a=>eval('for(o="",i=0;i<a;i++)o+=i;o')

이 함수는 코드를 래핑 eval하여 eval평가 에서 마지막 명령문을 작성 하여 중괄호와 return 문을 제거 했습니다 o. 이로 인해 evalreturn o이 리턴되고 o, 이제는 단일 명령문이므로 함수가 리턴 됩니다.

이 개선 된 시도의 무게는 38 바이트 이며 원래 바이트는 1 바이트입니다.

그러나 더 많은 것이 있습니다! 평가 문은 마지막 계산서에 평가 된 내용을 반환합니다. 이 경우로 o+=i평가 o되므로 ;o! (감사합니다, edc65!)

a=>eval('for(o="",i=0;i<a;i++)o+=i')

이 마지막 시도의 무게는 36 바이트에 불과 합니다. 원본보다 3 바이트가 절약되었습니다!


이 기술은 화살표 함수가 값을 반환하고 여러 명령문을 가질 필요가있는 일반적인 경우로 확장 될 수 있습니다 (다른 방법으로는 결합 할 수 없음)

b=>{statement1;statement2;return v}

된다

b=>eval('statement1;statement2;v')

바이트를 저장합니다.

statement2평가되면 다음 v과 같습니다.

b=>eval('statement1;statement2')

총 3 바이트를 절약합니다.


1
난 그냥도 짧아 질 수 익명 함수를 작성, 생각
Downgoat

@vihan 예,이 두 함수는 각각 2 바이트를 절약하기 위해 익명으로 만들 수 있습니다. 1 바이트 절약은 여전히 ​​유효합니다.
jrich

1
: 그러나 더 나은 평가는 필요가 없습니다, evalued 마지막 표현을 반환 ;o- 그것을 시도 :a=>eval('for(o="",i=0;i<a;i++)o+=i')
edc65

4
그러나 템플릿 문자열!
코너 O'Brien

1
@ CᴏɴᴏʀO'Bʀɪᴇɴ 예제 함수를 컨텍스트로 사용하여 템플릿 문자열이 어떻게 작동하는지 설명해 주시겠습니까?
WallyWest

10

"\ n"보다 템플릿 문자열 줄 바꾸기를 선호하십시오.

이것은 코드에서 줄 바꿈 문자 하나까지 지불하기 시작합니다. 하나의 사용 사례는 다음과 같습니다.

(16 바이트)

array.join("\n")

(15 바이트)

array.join(`
`)

업데이트 : 태그가 달린 템플릿 문자열 (덕분에 edc65!) 때문에 중괄호를 버릴 수도 있습니다.

(13 바이트)

array.join`
`

5
그러나 괄호를 피할 수 있습니다. 문서를 읽으십시오 ( developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… )
edc65 08/07/16

아 맞아 고마워 추가했습니다.
Chiru

9

채우기 배열-정적 값 및 동적 범위

나는 처음에 이것을 이해 아래 주석으로 남겨 두었지만, 그 게시물은 주로 이해에 초점을 두었 기 때문에, 나는 이것이 자신의 자리를 제공하는 것이 좋을 것이라고 생각했다.

ES6은 루프를 사용하지 않고 정적 값으로 배열을 채울 수있는 기능을 제공했습니다.

// ES5
function(x){for(i=0,a=[];i<x;i++)a[i]=0;return a}

// ES6
x=>Array(x).fill(0)

둘 다 값 0으로 채워진 길이 x의 배열을 반환합니다.

그러나 동적 값 (예 : 0 ... x의 범위)으로 배열을 채우려면 결과가 약간 길어집니다 (아직 이전 방식보다 짧음).

// ES5
function(x){for(i=0,a=[];i<x;i++)a[i]=i;return a}

// ES6
x=>Array(x).fill().map((a,i)=>i)

둘 다 값 0으로 시작하고 x-1로 끝나는 길이 x의 배열을 반환합니다.

.fill()거기에 필요한 이유 는 단순히 배열을 초기화하면 매핑 할 수 없기 때문입니다. 즉, 수행 x=>Array(x).map((a,i)=>i)하면 빈 배열이 반환됩니다. 스프레드 연산자를 사용하여 채우기 필요성을 극복하고 더 짧게 만들 수도 있습니다.

x=>[...Array(x)]

스프레드 연산자와 .keys()함수를 사용하여 짧은 0 ... x 범위를 만들 수 있습니다.

x=>[...Array(x).keys()]

x ... y의 맞춤 범위 또는 특수 범위 (예 : 짝수) 를 원하는 경우 스프레드 연산자를 .keys()사용 하여을 제거 .map()하거나 사용할 .filter()수 있습니다.

// Custom range from x...y
(x,y)=>[...Array(y-x)].map(a=>x++)

// Even numbers (using map)
x=>[...Array(x/2)].map((a,i)=>i*2)

// Even numbers (using filter)
x=>[...Array(x).keys()].filter(a=>~a%2)

두 번째 예에 대한 제안은 다음과 같습니다. x=>Array(x).fill(i=0).map(a=>i++)또한 0 in .fill(0)이 필요한지 확실하지 않습니다 . 당신은없이 그것을 시도 했습니까?
ETHproductions

@ETHproductions 당신이 맞습니다, 나는지도 전에 채우기에 0이 필요하지 않다는 것을 잊었습니다. 그래도 제안 된 것보다 1 문자가 짧아 지므로 계속 유지하겠습니다. 감사!
Mwr247

또한 마지막 예제의 a=>a%2-1경우처럼 잘 작동합니다 a=>a%2<1.
ETHproductions

1
내가 배운 새로운 트릭 : [...Array(x)]뿐만 아니라 작동 Array(x).fill()하고 2 바이트 짧습니다. x=>[...Array(x)].map((a,i)=>i)
ETHproductions

1
@yonatanmn 아주 좋아요! 주석 만 1) 1/4예제가 더 짧아지고 [0,0,0,0]2) 문자열 함수는 구현에 따라 다르므로 신뢰할 수있는 길이를 반환하지 않습니다 ( MapChrome에서는 32 바이트, Firefox에서는 36 바이트).
Mwr247

9

화살표 함수에서 값 반환

단일 명령문이 화살표 함수 선언 다음에 오는 경우 해당 명령문의 결과를 리턴한다는 것은 일반적인 지식입니다.

a=>{return a+3}
a=>a+3

-7 바이트

따라서 가능하면 여러 명령문을 하나로 결합하십시오. 괄호로 문장을 묶고 쉼표로 분리하면 가장 쉽게 수행 할 수 있습니다.

a=>{r=0;a.map(n=>r+=n);return r}
a=>(r=0,a.map(n=>r+=n),r)

-8 바이트

그러나 성명서가 두 개 뿐인 경우 일반적으로 성명을 &&또는 로 결합하는 것이 가능하고 짧습니다 ||.

a=>{r=0;a.map(n=>r+=n);return r}

// - Use && because map always returns an array (true)
// - declaration of r moved into unused map argument to make it only 2 statements
a=>a.map(n=>r+=n,r=0)&&r

-9 바이트

마지막으로 map (또는 이와 유사한)을 사용하고 숫자를 반환해야하고지도가 숫자가있는 1 길이 배열을 반환하지 않도록 보장 할 수있는 경우 다음을 사용하여 숫자를 반환 할 수 있습니다 |.

a=>{a=b=0;a.map(n=>(a+=n,b-=n));return a/b}

// - {} in map ensures it returns an array of undefined, so the | will make the returned
//   array cast from [ undefined, undefined, undefined ] to ",," to NaN to 0 and 0|n = n,
//   if the map returned [ 4 ] it would cast from [ 4 ] to "4" to 4 and make it 4|n
a=>a.map(n=>{a+=n,b-=n},a=b=0)|a/b

마지막 예에서는 숫자가 항상 정수인지 확인해야합니다.
ETHproductions

8

임의의 템플릿 문자열 해킹

이 함수는 두 문자열을 리플 링합니다 (예 : "abc","de"로 바 "adbec")).

f=(x,y)=>String.raw({raw:x},...y)

x이보다 긴 경우 에만 작동합니다 y. 어떻게 작동합니까? String.raw다음과 같이 템플릿 태그로 설계되었습니다.

String.raw`x: ${x}\ny: ${y}\nx + y: ${x + y}`

String.raw(["x: ", "\ny: ", "\nx + y: ", ""], x, y, x + y)간단하지는 않지만 기본적으로을 호출 합니다. 템플릿 배열에는 raw기본적으로 배열의 복사본이지만 원시 문자열 이있는 특수 속성이 있습니다. String.raw(x, ...args)기본적으로 항목이 부족할 x.raw[0] + args[0] + x.raw[1] + args[1] + x.raw[2] + ...때까지 계속 돌아갑니다 x.

이제 String.raw작동 방식 을 알았 으므로 이점을 활용할 수 있습니다.

f=(x,y)=>String.raw({raw:x},...y)                   // f("abc", "de") => "adbec"
f=x=>String.raw({raw:x},...[...x].keys())           // f("abc") => "a0b1c"
f=(x,y)=>String.raw({raw:x},...[...x].fill(y))      // f("abc", " ") => "a b c"

물론, 마지막 f=(x,y)=>x.split``.join(y)것은 짧아 지지만 아이디어는 얻습니다.

여기에 또한 경우 작동 기능 riffling의 부부 xy동일한 길이있다는 :

f=(x,y)=>String.raw({raw:x.match(/.?/g)},...y)
f=(x,y)=>String.raw({raw:x},...y)+y.slice(-1)  // Only works if x.length == y.length

당신은에 대해 자세히 배울 수 String.raw MDN에를 .


7

재귀와 골프하는 방법

가장 빠른 옵션은 아니지만 재귀가 가장 짧습니다. 일반적으로 솔루션이 문제의 작은 부분으로 솔루션을 단순화 할 수있는 경우 (특히 입력이 숫자 또는 문자열 인 경우) 재귀가 가장 짧습니다. 예를 들어 and 에서 f("abcd")계산할 수있는 경우 일반적으로 재귀를 사용하는 것이 가장 좋습니다."a"f("bcd")

예를 들어 계승을 보자.

n=>[...Array(n).keys()].reduce((x,y)=>x*++y,1)
n=>[...Array(n)].reduce((x,_,i)=>x*++i,1)
n=>[...Array(n)].reduce(x=>x*n--,1)
n=>{for(t=1;n;)t*=n--;return t}
n=>eval("for(t=1;n;)t*=n--")
f=n=>n?n*f(n-1):1

이 예제에서 재귀는 다른 옵션보다 훨씬 짧습니다.

문자 코드의 합은 어떻습니까?

s=>[...s].map(x=>t+=x.charCodeAt(),t=0)|t
s=>[...s].reduce((t,x)=>t+x.charCodeAt())
s=>[for(x of(t=0,s))t+=x.charCodeAt()]|t  // Firefox 30+ only
f=s=>s?s.charCodeAt()+f(s.slice(1)):0

이것은 까다 롭지 만 올바르게 구현하면 재귀가 4 바이트를 절약한다는 것을 알 수 있습니다 .map.

이제 다른 유형의 재귀를 살펴 보겠습니다.

사전 재귀

일반적으로 가장 짧은 재귀 유형입니다. 입력은 두 부분으로 분할 a하고 b, 상기 함수에 무언가를 산출 a하고 f(b). 우리의 팩토리얼 예제로 돌아가서 :

f=n=>n?n*f(n-1):1

이 경우, aN , bN-1 , 및 리턴 값이다 a*f(b).

중요 사항 : 모든 재귀 함수 에는 입력이 충분히 작을 때 재귀 를 중지 할 수있는 방법이 있어야합니다 . 계승 함수에서 이는로 제어됩니다. n? :1즉, 입력이 0 이면 다시 호출하지 않고 1 을 반환 f합니다.

재귀 후

재귀 후 재귀는 사전 재귀와 비슷하지만 약간 다릅니다. 입력은 두 부분으로 분할 a하고 b, 상기 함수에 무언가를 산출 a한 후, 호출 f(b,a). 두 번째 인수는 일반적으로 기본값 (예 :)을 갖습니다 f(a,b=1).

사전 재귀는 최종 결과로 특별한 것을 수행해야 할 때 좋습니다. 예를 들어, 숫자의 계승에 1을 더한 경우

f=(n,p=1)=>n?f(n-1,n*p):p+1

그럼에도 불구하고 post-가 다른 함수 내에서 pre-recursion을 사용하는 것보다 항상 짧은 것은 아닙니다.

n=>(f=n=>n?n*f(n-1):1)(n)+1

언제가 더 짧습니까? 이 예제에서 사후 재귀에는 함수 인수 주위에 괄호가 필요하지만 사전 재귀에는 그렇지 않은 것을 알 수 있습니다. 일반적으로 두 솔루션 모두 인수 주위에 괄호가 필요한 경우 재귀 후의 크기는 약 2 바이트 짧습니다.

n=>!(g=([x,...a])=>a[0]?x-a.pop()+g(a):0)(n)
f=([x,...a],n=0)=>a[0]?f(a,x-a.pop()+n):!n

( 이 답변 에서 가져온 프로그램 )

가장 짧은 솔루션을 찾는 방법

일반적으로 가장 짧은 방법을 찾는 유일한 방법은 모든 방법을 시도하는 것입니다. 여기에는 다음이 포함됩니다.

  • 루프
  • .map(문자열의 경우 [...s].map또는 s.replace;의 경우 범위를 만들 수 있음 )
  • 배열 이해
  • 사전 재귀 (때때로 이러한 옵션 중 다른 옵션 내에서)
  • 재귀 후

그리고 이것들은 가장 일반적인 해결책입니다. 가장 좋은 해결책은 이들의 조합이거나 완전히 다른 것일 수 있습니다 . 가장 짧은 솔루션을 찾는 가장 좋은 방법은 모든 것을 시도하는 것 입니다.


1
그것의 가치를 위해 +1, 그리고 Zootopia를위한 또 다른 +1을 추가하고 싶습니다
edc65

7

더 짧은 방법 .replace


하나의 정확한 부분 문자열의 모든 인스턴스를 문자열의 다른 부분으로 바꾸려면 다음과 같이하십시오.

f=s=>s.replace(/l/g,"y") // 24 bytes
f("Hello, World!")       // -> "Heyyo, Woryd!"

그러나 1 바이트 더 짧을 수 있습니다.

f=s=>s.split`l`.join`y`  // 23 bytes
f("Hello, World!")       // -> "Heyyo, Woryd!"

g플래그 외에 정규식 기능을 사용하려는 경우 더 이상 짧지 않습니다 . 그러나 변수의 모든 인스턴스를 바꾸는 경우 일반적으로 훨씬 짧습니다.

f=(s,c)=>s.replace(RegExp(c,"g"),"") // 36 bytes
f=(s,c)=>s.split(c).join``           // 26 bytes
f("Hello, World!","l") // -> "Heo, Word!"

때로는 문자열의 각 문자를 다른 문자로 바꾸어 매핑하려고 할 수도 있습니다. 나는 종종 나 자신이 이것을하는 것을 발견 :

f=s=>s.split``.map(x=>x+x).join`` // 33 bytes
f=s=>[...s].map(x=>x+x).join``    // 30 bytes
f("abc") // -> "aabbcc"

그러나 .replace거의 항상 더 짧습니다.

f=s=>s.replace(/./g,x=>x+x)  // 27 bytes
f=s=>s.replace(/./g,"$&$&")  // Also works in this particular case

이제 문자열의 각 문자를 매핑하고 결과 문자열을 신경 쓰지 않으 .map려면 일반적으로 다음을 제거 할 수 있기 때문에 더 좋습니다 .join``.

f=s=>s.replace(/./g,x=>t+=+x,t=0)&&t // 36 bytes
f=s=>[...s].map(x=>t+=+x,t=0)&&t     // 32 bytes
f("12345")  // -> 15

마지막 경우 정규 표현식 (예 :)과 일치하는 특정 문자 만 /\w/g관심이 있다면 이 데모에서와 같이 바꾸기를 사용하는 것이 훨씬 좋습니다 .
Shieru Asakoto

6

로 정규식 리터럴 작성 eval

정규식 생성자는 이름이 길기 때문에 부피가 매우 클 수 있습니다. 대신, eval과 backticks로 리터럴을 작성하십시오.

eval(`/<${i} [^>]+/g`)

변수 i가와 같으면 foo다음을 생성합니다.

/<foo [^>]+/g

이것은 다음과 같습니다.

new RegExp("<"+i+" [^>]+","g")

String.raw백 슬래시를 반복적으로 피할 필요가 없도록 사용할 수도 있습니다.\

eval(String.raw`/\(?:\d{4})?\d{3}\d{3}\d{3}\d{3}\d{3}\d{3}\d{4}/g`)

출력됩니다 :

/(?:\d{4})?\d{3}\d{3}\d{3}/g

다음과 같습니다.

RegExp("\\(?:\\d{4})?\\d{3}\\d{3}\\d{3}\\d{3}\\d{3}\\d{3}\\d{4}","g")

명심하십시오!

String.raw많은 바이트를 차지하며 적어도 9 개의 백 슬래시 가 없으면 String.raw더 길어집니다.


당신은 new거기에 필요하지 않으므로 생성자를 사용하는 것이 실제로 두 번째 예에서 짧습니다
Optimizer

5

.forEachvs for루프

항상 .mapfor 루프를 선호 하십시오. 쉽고 즉각적인 절약.


a.map(f)
for(x of a)f(x);
for(i=0;i<a.length;)f(a[i++]);
  • 원본의 총 8 바이트
  • 8 바이트 절약 (for-of) ( 50 % 감소)
  • C 스타일 for 루프에 비해 22 바이트 절약 ( 73 % 감소)

a.map(x=>f(x,0))
for(x of a)f(x,0);
for(i=0;i<a.length;)f(a[i++],0);
  • 원본의 경우 총 16 바이트
  • 2 바이트 절약 vs. ( 11 % 감소)
  • C 스타일 for 루프에 비해 16 바이트 절약 ( 50 % 감소)

a.map((x,i)=>f(x,i,0))
for(i in a)f(a[i],i,0);
for(i=0;i<a.length;)f(a[i],i++,0);
  • 원래 총 22 바이트
  • 1 바이트 절약 대 대비 ( 4 % 감소)
  • C 스타일 for 루프에 비해 11 바이트 절약 ( 33 % 감소)

a.map(x=>f(x)&g(x))
for(x of a)f(x),g(x);
for(i=0;i<a.length;)f(x=a[i++]),g(x);
  • 원본의 경우 총 19 바이트
  • 2 바이트 절약 vs. 10 % 감소
  • C 스타일 for 루프에 비해 18 바이트 절약 ( 49 % 감소)

5

재귀에서 초기화되지 않은 카운터 사용

참고 : 엄밀히 말하면 이것은 ES6에만 국한되지 않습니다. 그러나 화살표 함수의 간결한 특성으로 인해 ES6에서 재귀를 사용하고 남용하는 것이 더 합리적입니다.


k처음에는 0으로 설정되고 각 반복마다 증가 하는 카운터를 사용하는 재귀 함수를 만나는 것이 일반적입니다 .

f = (…, k=0) => [do a recursive call with f(…, k+1)]

특정 상황에서 이러한 카운터의 초기화를 생략하고 다음과 같이 대체 할 수 k+1있습니다 -~k.

f = (…, k) => [do a recursive call with f(…, -~k)]

이 트릭은 일반적 으로 2 바이트를 절약합니다 .

왜 그리고 언제 작동합니까?

가능하게하는 공식은 ~undefined === -1입니다. 따라서 첫 번째 반복에서는 -~k로 평가됩니다 1. 다음 반복에서, 최소한 [0… 2 31 -1] 범위의 정수에 대해 equal 와 -~k본질적으로 같습니다 .-(-k-1)k+1

그러나 k = undefined첫 번째 반복이 기능의 작동을 방해하지 않도록해야합니다. 특히 대부분의 산술 연산 undefined은 결과를 초래 한다는 점을 명심해야합니다 NaN.

실시 예 # 1

양의 integer가 주어지면 n이 함수는 k나누지 않는 가장 작은 정수 를 찾습니다 n.

f=(n,k=0)=>n%k?k:f(n,k+1)   // 25 bytes

다음과 같이 단축 할 수 있습니다.

f=(n,k)=>n%k?k:f(n,-~k)     // 23 bytes

때문에 작품 n % undefined이다 NaNfalsy이다. 이것이 첫 번째 반복에서 예상되는 결과입니다.

[원래 답변으로 연결]

실시 예 # 2

양의 정수가 주어지면 n이 함수는 다음 p과 같은 정수를 찾습니다 (3**p) - 1 == n.

f=(n,p=0,k=1)=>n<k?n>k-2&&p:f(n,p+1,k*3)  // 40 bytes

다음과 같이 단축 할 수 있습니다.

f=(n,p,k=1)=>n<k?n>k-2&&p:f(n,-~p,k*3)    // 38 bytes

이것은 p첫 번째 반복에서 전혀 사용되지 않기 때문에 작동합니다 ( n<k거짓).

[원래 답변으로 연결]


5

ES6 기능

수학

Math.cbrt(x)보다 문자를 저장합니다 Math.pow(x,1/3).

Math.cbrt(x)
Math.pow(x,1/3)

3 문자 저장

Math.hypot(...args)arg의 제곱의 합의 제곱근이 필요할 때 유용합니다. ES5 코드를 만드는 것은 내장을 사용하는 것보다 훨씬 어렵습니다.

이 기능 은 짧을수록 Math.trunc(x)도움이되지 않습니다 x|0. (Mwr247 감사합니다!)

ES5에서는 많은 코드를 사용하지만 ES6에서는 더 쉬운 많은 속성이 있습니다.

  • Math.acosh, asinh, atanh, cosh, sinh, tanh. 삼각 함수에 해당하는 쌍곡선을 계산합니다.
  • Math.clz32. ES5에서 할 수 있지만 지금은 더 쉽습니다. 숫자의 32 비트 표현에서 선행 0을 계산합니다.

이 많은, 그래서 난 그냥 몇 가지를 나열거야 :
Math.sign, Math.fround, Math.imul, Math.log10, Math.log2, Math.log1p.


Math.trunc(x)보다 4 배 더 깁니다 x|0.
Mwr247

@ mwr247 : 좋아, 업데이트됩니다.
ev3commander

여기에 이러한 기능의 몇 내가 아는 가장 짧은 ES5의 등가물이다 : Math.hypot(a,b) => Math.sqrt(a*a+b*b)(이상 3 바이트, 더 인수도 길어집니다) Math.sign(a) => (a>0)-(a<0)(1 바이트 짧은,하지만 어떤 경우에는 괄호를 둘러싼 필요; 작동하지 않을 수와 NaN)
ETHproductions

@ETHproductions 하이트 (es5 해결 방법)에 대한 인수 배열이 필요합니다. 그리고 Math.sign에 대한 해결 방법이 -0에서 작동합니까? (-0을 반환해야 함)
ev3commander

1
@ ev3commander 이들은 각각의 ES6 동등 물을 인라인 대체품으로 사용하기 때문에 99 % 사용으로 축소됩니다. 이 함수들을 실제로 재생성하려면 훨씬 더 많은 코드가 필요합니다. 또한 (AFAIK)는 수동으로 지정하지 않고 -0을 얻을 수있는 방법이 없으며 실제로 코드 골프 내에서 사용하지 않기 때문에 -0에 특별한 경우가 필요하지 않습니다. 그러나 그 점을 지적 해 주셔서 감사합니다.
ETHproductions

5

작은 상수 범위 최적화 map()

문맥

map()for[0..N1]

for(i = 0; i < 10; i++) {
  do_something_with(i);
}

다음 중 하나로 대체 될 수 있습니다.

[...Array(10).keys()].map(i => do_something_with(i))

또는 더 일반적으로 :

[...Array(10)].map((_, i) => do_something_with(i))

Array(N)N

범위에 대한 최적화[0..N1]

i

N           | Method                               | Example                         | Length
------------+--------------------------------------+---------------------------------+-------
N ≤ 6       | use a raw array of integers          | [0,1,2,3].map(i=>F(i))          | 2N+10
N = 7       | use either a raw array of integers   | [0,1,2,3,4,5,6].map(i=>F(i))    | 24
            | or a string if your code can operate | [...'0123456'].map(i=>F(i))     | 23
            | with characters rather than integers |                                 |
8 ≤ N ≤ 9   | use scientific notation 1e[N-1]      | [...1e7+''].map((_,i)=>F(i))    | 24
N = 10      | use scientific notation 1e9          | [...1e9+''].map((_,i)=>F(i))    | 24
            | or the ES7 expression 2**29+'4' if   | [...2**29+'4'].map(i=>F(i))     | 23
            | the order doesn't matter and your    |                                 |
            | code can operate with characters     |  (order: 5,3,6,8,7,0,9,1,2,4)   |
            | rather than integers                 |                                 |
11 ≤ N ≤ 17 | use scientific notation 1e[N-1]      | [...1e12+''].map((_,i)=>F(i))   | 25
N = 18      | use the fraction 1/3                 | [...1/3+''].map((_,i)=>F(i))    | 24
N = 19      | use the fraction 1/6                 | [...1/6+''].map((_,i)=>F(i))    | 24
20 ≤ N ≤ 21 | use scientific notation 1e[N-1]      | [...1e20+''].map((_,i)=>F(i))   | 25
N = 22      | use scientific notation -1e20        | [...-1e20+''].map((_,i)=>F(i))  | 26
23 ≤ N ≤ 99 | use Array(N)                         | [...Array(23)].map((_,i)=>F(i)) | 27

NB : 콜백 코드의 길이는 F(i)계산되지 않습니다.

를 사용하여 범위에 대한 최적화[1..9]

[1..9]

[...17**6+'8'].map(i=>F(i))  // order: 2,4,1,3,7,5,6,9,8; length: 23

카운터없는 최적화

N

N           | Method                               | Example                         | Length
------------+--------------------------------------+---------------------------------+-------
N ≤ 5       | use a raw array of integers          | [0,0,0,0].map(_=>F())           | 2N+10
6 ≤ N ≤ 10  | use scientific notation 1e[N-1]      | [...1e7+''].map(_=>F())         | 20
11 ≤ N ≤ 17 | use scientific notation 1e[N-1]      | [...1e12+''].map(_=>F())        | 21
N = 18      | use the fraction 1/3                 | [...1/3+''].map(_=>F())         | 20
N = 19      | use the fraction 1/6                 | [...1/6+''].map(_=>F())         | 20
20 ≤ N ≤ 21 | use scientific notation 1e[N-1]      | [...1e20+''].map(_=>F())        | 21
N = 22      | use scientific notation -1e20        | [...-1e20+''].map(_=>F())       | 22
23 ≤ N ≤ 99 | use Array(N)                         | [...Array(23)].map(_=>F())      | 23

NB : 콜백 코드의 길이는 F()계산되지 않습니다.


2**262**29?
얽히고 설킨

@Shaggy Heck. 잘 잡아!
Arnauld

코드 맹인 때문에 스스로 편집하고 싶지 않았습니다! : D
얽히고 설킨

를 사용하면 .keys()람다가 필요하지 않습니다.[...Array(10).keys()].map(do_something_with)
long-lazuli

@ long-lazuli 람다가 필요하지 않고 그냥 범위를 원한다면 아마도지도가 필요하지 않을 것입니다 ...
Arnauld

4

할당 해체

ES6에서는 값을 조각으로 자르고 각 조각을 다른 변수에 할당하는 것과 같이 할당을 파괴하는 새로운 구문을 도입했습니다. 몇 가지 예는 다음과 같습니다.

문자열과 배열

a=s[0];b=s[1];       // 14 bytes
[a,b]=s;             //  8 bytes

a=s[0];s=s.slice(1); // 20 bytes
a=s.shift();         // 12 bytes, only works if s is an array
[a,...s]=s;          // 11 bytes, converts s to an array

사물

a=o.asdf;b=o.bye;c=o.length; // 28 bytes
{asdf:a,bye:b,length:c}=o;   // 26 bytes

a=o.a;b=o.b;c=o.c; // 18 bytes
{a,b,c}=o;         // 10 bytes

이러한 할당은 기능 매개 변수에서도 사용할 수 있습니다.

f=a=>a[0]+a[1]+a[2]
f=([a,b,c])=>a+b+c

f=b=>b[1]?b[0]+f(b.slice(1)):b[0]*2
f=b=>b[1]?b.shift()+f(b):b[0]*2
f=([a,...b])=>b[0]?a+f(b):a*2

4

피하는 또 다른 방법 return

여러 명령문과 리턴이있는 화살표 함수에 eval을 사용해야한다는 것을 알고 있습니다 . 비정상적인 경우 내부 하위 기능을 사용하여 더 많은 비용을 절약 할 수 있습니다.

나는 비정상적인 말 때문에

  1. 리턴 된 결과는 루프에서 마지막으로 계산 된 표현식이 아니어야합니다.

  2. 루프 전에 (최소한) 2 개의 다른 초기화 가 있어야합니다

이 경우 초기 값 중 하나를 매개 변수로 전달하여 반환없이 내부 하위 함수를 사용할 수 있습니다.

a에서 b 사이의 값에 대한 exp의 합 함수의 역수를 구합니다.

먼 길-55 바이트

(a,b)=>{for(r=0,i=a;i<=b;i++)r+=Math.exp(i);return 1/r}

eval-54 바이트

(a,b)=>eval("for(r=0,i=a;i<=b;i++)r+=Math.exp(i);1/r")

내부 함수-53 바이트

(a,b)=>(i=>{for(r=0;i<=b;i++)r+=Math.exp(i)})(a)||1/r

더 낮은 범위 제한이 필요하지 않으면 ai 및 r의 초기화를 병합 할 수 있으며 평가 버전이 더 짧습니다.


샘플에 보관할 필요가 없습니다a
l4m2

@ l4m2 나는 당신의 요점을 얻을 수 없습니다, 도와주세요 ...
edc65

(i,b)=>{for(r=0;i<=b;i++)r+=Math.exp(i);return 1/r}
l4m2

@ l4m2 어, return a/r더 나은 예가 될 것입니다
edc65

1
eval은 여전히 (a,b)=>1/eval("for(r=0,i=a;i<=b;i++)r+=Math.exp(i)")(i,b)=>1/eval("for(r=0;i<=b;)r+=Math.exp(i++)")
낫고이

4

Dyadic 및 재귀 함수에 카레 구문 사용

이차 함수

함수가 기본값없이 정확히 두 개의 인수를 취할 때마다 커링 구문을 사용하면 1 바이트가 절약됩니다.

전에

f =
(a,b)=>a+b  // 10 bytes

로 전화 f(a,b)

f =
a=>b=>a+b   // 9 bytes

로 전화 f(a)(b)

참고 : 메타의이 게시물 은이 구문의 유효성을 확인합니다.

재귀 함수

카레 구문을 사용하면 재귀 함수가 여러 인수를 취하지 만 각 반복 사이에서 일부만 업데이트해야 할 때 일부 바이트를 절약 할 수 있습니다.

다음 함수는 범위 내의 모든 정수의 합을 계산합니다 [a,b].

f=(a,b)=>a>b?0:b+f(a,b-1)   // 25 bytes

a전체 프로세스 중에 변경되지 않은 상태로 유지 되므로 다음을 사용하여 3 바이트를 절약 할 수 있습니다.

f =                         // no need to include this assignment in the answer anymore
a=>F=b=>a>b?0:b+F(b-1)      // 22 bytes

참고 : 의견에서 Neil이 알 수 있듯이 인수가 재귀 함수에 명시 적으로 전달되지 않았다는 것이 불변으로 간주되어야한다는 것을 의미하지는 않습니다. 필요한 경우, 우리는 수정할 수 a함수와 코드 내에서 a++, a--또는 그와 유사한 어떤 구문.


마지막 예는로 작성 될 수 있으며 각 재귀 호출에 대해 a=>F=b=>a>b?0:a+++F(b)수정 a됩니다. 이 경우에는 도움이되지 않지만 더 많은 인수가있는 경우 바이트를 절약 할 수 있습니다.
Neil

Heh, 나는 단지 이것에 대한 팁을 쓰는 것에 대해 생각하고 있었다 :-)
ETHproductions

4

우선 성 테스트 기능

다음 28 바이트 함수는 true소수와 false비 프라임에 대해 리턴 합니다 .

f=(n,x=n)=>n%--x?f(n,x):x==1

다른 것을 계산하기 위해 쉽게 수정할 수 있습니다. 예를 들어이 39 바이트 함수는 소수 이하의 소수를 계산합니다.

f=(n,x=n)=>n?n%--x?f(n,x):!--x+f(n-1):0

기본 성 n을 검사하려는 변수 가 이미있는 경우 기본 기능을 상당히 단순화 할 수 있습니다.

(f=x=>n%--x?f(x):x==1)(n)

작동 원리

f = (         // Define a function f with these arguments:
  n,          //   n, the number to test;
  x = n       //   x, with a default value of n, the number to check for divisibility by.
) =>
  n % --x ?   //   If n is not divisible by x - 1,
  f(n, x)     //     return the result of f(n, x - 1).
              //   This loops down through all numbers between n and 0,
              //     stopping when it finds a number that divides n.
  : x == 1    //   Return x == 1; for primes only, 1 is the smallest number
              //     less than n that divides n.
              //   For 1, x == 0; for 0, x == -1.

참고 : 12345와 같이 충분히 큰 입력으로 호출하면 "너무 많은 재귀"오류와 함께 실패합니다. 루프를 사용하여이 문제를 해결할 수 있습니다.

f=n=>eval('for(x=n;n%--x;);x==1')

1
그러나 12345에
불과한

x==1아마 x<2저축을위한 것일 수 있습니다 .
CalculatorFeline

@CalculatorFeline 감사합니다.하지만 실패합니다. 1 하거나 0(때문에 x0또는 -1각각)
ETHproductions

특정 경우에 유용 할 수 있습니다. 또한 !~-x-0 바이트의 경우.
CalculatorFeline

3

Array#concat() 스프레드 연산자

상황에 따라 크게 달라집니다.


여러 배열 결합.

복제하지 않는 한 concat 기능을 선호하십시오.

0 바이트 저장

a.concat(b)
[...a,...b]

3 바이트 낭비

a.concat(b,c)
[...a,...b,...c]

3 바이트 저장

a.concat()
[...a]

6 바이트 저장

// Concatenate array of arrays
[].concat.apply([],l)
[].concat(...l)

기존 배열을 사용하는 것을 선호합니다 Array#concat().

간편한 4 바이트 저장

[].concat(a,b)
a.concat(b)

3

중간 결과 반환

쉼표 연산자를 사용하면 마지막 값을 반환하는 일련의 식을 실행할 수 있습니다. 그러나 리터럴 배열 구문을 남용하면 중간 값을 반환 할 수 있습니다. 예를 들어 .map ()에서 유용합니다.

// capitalize words
// f is a flag indicating if prev char is space
[...x].map(c=>(f?c=c.toUpperCase():0,f=c<'!',c),f=1).join('')

// shortened to ...
[...x].map(c=>[f?c.toUpperCase():c,f=c<'!'][0],f=1).join('')

3
기억 물론, 그 .join('')수 있습니다.join``
Cyoce

3

기능 매개 변수 기본값 설정

($,a,b,_)=>_!=undefined?'asdf':_ // before
($,a,b,_)=>_!=[]._?'asdf':_ // before, but a bit golfed
($,a,b,_='asdf')=>_ // after

이것은 정말 유용합니다 ...

그러나 _=>_||'asdf'함수에 하나의 (유용한) 인수 만 전달할 때 와 같은 것이 더 짧다는 것을 이해해야 합니다.


1
_=>_||'asdf'대부분의 경우 OR 사용 이 일반적으로 더 짧습니다.
Downgoat

@Downgoat (빈 문자열) "asdf"의 입력 을 반환한다는 점에 유의하십시오 "".
ETHproductions

2
undefined해당 값을 명시 적으로 전달하더라도 인수가 일 때마다 기본값이 평가 됩니다. 예를 들어, [...Array(n)].map((a,b,c)=>b)항상 통과 undefined를 위해 a, 당신은 때문에 (하지의 측면에서 비록 그것에 대한 디폴트 값을 제공 할 수 있습니다 b).
Neil

3

eval화살표 기능에 중괄호 대신 사용

화살표 기능은 훌륭합니다. 인수는 형식 x=>y이며 반환 값입니다. 그러나와 같은 제어 구조를 사용해야하는 경우 괄호 를 사용해야합니다 ( 예 :) . 그러나이 문제를 해결할 수 있습니다. 운 좋게도 함수는 문자열을 가져와 해당 문자열을 JS 코드로 평가 한 다음 마지막으로 평가 된 expression을 반환합니다 . 예를 들어 다음 두 가지를 비교하십시오.xywhile=>{while(){};return}eval

x=>{while(foo){bar};return baz} // before
x=>eval('while(foo){bar};baz')  // after
//                            ^

우리는이 개념의 확장을 사용하여 코드를 더 짧게 할 수 있습니다 eval. 예를 들면 다음과 같습니다.

x=>{while(foo)bar++;return bar} // before
x=>eval('while(foo)++bar')      // after
//                        ^^^^^

3

ES6의 골프 논리 연산

"GLOE (S6)"

일반 논리

당신이 문을 구축 한 말 st. 다음 교체품을 사용할 수 있는지 확인하십시오.

Traditional conjuction: s&&t
Equivalent conjuction: s*t OR s&t

Traditional disjunction: s||t
Equivalent disjunction: s+t OR s|t

(; 즉 이러한 순서가 잘못되면 작동하지 않을 수 있습니다 +*보다 낮은 순서로 우선 순위를 가지고 ||&&않습니다.)

또한 다음은 편리한 논리식입니다.

  • 하나 s또는 t참 / XOR입니다 :s^t
  • s그리고 t같은 진실 가치입니다 : !s^t또는s==t

배열 논리

a조건 을 만족시키는 모든 구성원 p:

a.every(p)                             // 10 bytes (11 bytes saved)
a.map(x=>c&=p(x),c=1)                  // 21 bytes (16 bytes saved)
for(i=0,c=1;i<a.length;c&=p(a[i++]));  // 37 bytes (hideously long)

구성원 중 하나 이상이 a조건 을 충족합니다 p.

a.some(p)                            // 9  bytes (13 bytes saved)
a.map(x=>c|=p(x),c=0)                // 21 bytes (14 bytes saved)
for(i=c=0;i<a.length;c|=p(a[i++]));  // 35 bytes (just please no)

의 어떤 멤버 없습니다 a충족 조건 p: !a.some(p).

요소 e가 배열에 존재합니다 a:

a.includes(e)                        // 13 bytes, standard built-in
~a.indexOf(e)                        // 13 bytes, "traditional" method
a.find(x=>e==x)                      // 15 bytes, find (ES6)
a.some(x=>x==e)                      // 15 bytes, some (ES5)
(a+"").search(e)                     // 16 bytes, buggy
a.filter(t=>t==e).length             // 24 bytes, no reason to use this
for(i=c=0;i<a.length;c+=e==a[i++]);  // 35 bytes, super-traditional

배열에 요소 e없습니다a :

!a.includes(e)
!~a.indexOf(e)
a.every(t=>t!=e)
!a.filter(t=>t==e).length
for(i=0,c=1;i<a.length;c*=e!=a[i++]);

나는 일반적으로 사용 &&하고 ||같은 x?y:xx?x:y각각. 그러나 더 많은 논리 기반 프로그램에서 이것이 어떻게 유용한 지 알 수 있습니다. 가진 하나의 문제는 +그 예를 들어 것 3하고 -3모두 truthy, 그러나 3+-3아니다.
ETHproductions

@ETHproductions 아, 맞아; 이것이 엣지 케이스입니다. -경우에도 작동 할 수 있습니다 s != t.
코너 오브라이언

a.filter(t=>t==e).length==a.length부정확하다. 그것은해야한다!a.filter(t=>t==e).length
ETHproductions

@ETHproductions 맞습니다!
Conor O'Brien

3

반복되는 함수 호출 단축

캔버스 조작과 같이 긴 이름을 가진 함수를 반복해서 호출 한 경우 :

c.lineTo(0,100);c.lineTo(100,100);c.lineTo(100,0);c.lineto(0,0);c.stroke()

단축하는 전통적인 방법은 함수 이름의 별명을 지정하는 것입니다.

c[l='lineTo'](0,100);c[l](100,100);c[l](100,0);c[l](0,0);c.stroke()

충분한 호출이있는 경우 더 나은 방법은 다음 작업을 수행하는 함수를 작성하는 것입니다.

l=(x,y)=>c.lineTo(x,y);l(0,100);l(100,100);l(100,0);l(0,0);c.stroke()

대부분의 함수 호출이 연결되어있는 경우 함수가 자체적으로 리턴되도록하여 각 연속 호출에서 2 바이트를 줄일 수 있습니다.

l=(x,y)=>c.lineTo(x,y)||l;l(0,100)(100,100)(100,0)(0,0);c.stroke()

사용법 예 : 1 , 2


1
bind 연산자를 사용 하여 단축 할 수 있습니다 .(l=::c.lineTo)(0,100)(100,100)(100,0)(0,0);c.stroke()
Downgoat

@ Downgoat 고맙습니다. 어떤 브라우저에서 지원됩니까? (또한, 내가 본 것에서 두 번째 호출에서 오류가 발생하기 때문에 c.lineTo자연스럽게 자체 반환되지 않기 때문에 )
ETHproductions

ES7 기능이므로 babel을 통해 문질러 야합니다.
Downgoat

3

바인드 연산자 ::

바인드 연산자를 사용하면 반복되는 함수보다 바이트를 줄일 수 있습니다.

(x='abc'.search(a))+x.search(b) // Before
(x=::'abc'.search)(a)+x(b)      // 5 bytes saved

또한 다른 기능과 함께 기능을 사용하려는 경우 this:

s[r='replace'](/a/g,'b')+s[r](/c/g,'d') // Before
(r=s.replace)(/a/g,'b')+s::r(/c/g,'d')  // 1 byte saved

3

많은 데이터를 저장할 때 쉼표 피하기

배열에 저장해야하는 많은 데이터 (예 : 인덱스, 문자 등)가있는 경우 모든 쉼표를 남기지 않는 것이 좋습니다. 이것은 모든 데이터 조각이 동일한 문자열 길이를 갖는 경우에 가장 효과적이며, 1은 분명히 최적입니다.

43 바이트 (기준)

a=[[3,7,6,1,8,9,4,5,2],[5,4,3,2,7,6,5,4,3]]

34 바이트 (쉼표 없음)

a=[[..."376189452"],[..."543276543"]]

당신이 기꺼이 배열 액세스 변경 동일한 값을 다음과 같이 저장하여 더 줄일 수 있습니다.

27 바이트 (동일한 데이터, 어레이 액세스 만 변경)

a=[..."376189452543276543"]

마지막 블록 만 강조 표시되는 이유는 무엇입니까?
CalculatorFeline

@CalculatorFeline 감사합니다.
Chiru
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.