Mẹo chơi gôn trong ECMAScript 6 trở lên


88

Điều này tương tự như "Mẹo chơi gôn trong <...>" khác, nhưng đặc biệt nhắm mục tiêu các tính năng mới hơn trong JavaScript được đưa ra trong ECMAScript 6 trở lên.

JavaScript vốn đã là một ngôn ngữ rất dài dòng, function(){}, .forEach(), chuyển đổi chuỗi mảng, mảng giống như đối tượng để mảng, vv, vv là siêu bloats và không lành mạnh cho sân golf.

ES6 +, mặt khác, có một số tính năng siêu tiện dụng và giảm dấu chân. x=>y, [...x]vv chỉ là một số ví dụ.

Vui lòng đăng một số thủ thuật hay có thể giúp loại bỏ một vài byte thừa từ mã của bạn.

LƯU Ý: Thủ thuật cho ES5 đã có sẵn trong Mẹo để chơi gôn trong JavaScript ; câu trả lời cho chủ đề này nên tập trung vào các thủ thuật chỉ có trong ES6 và các phiên bản ES tương lai khác.

Tuy nhiên, chủ đề này cũng dành cho người dùng hiện đang sử dụng các tính năng ES5. Câu trả lời cũng có thể chứa các mẹo để giúp họ hiểu và ánh xạ các tính năng ES6 theo phong cách mã hóa ES5 của họ.

Câu trả lời:


42

Toán tử trải ...

Toán tử trải biến đổi một giá trị mảng thành một danh sách được phân tách bằng dấu phẩy.

Trường hợp sử dụng 1:

Sử dụng trực tiếp một mảng trong đó một hàm mong đợi một danh sách

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

Trường hợp sử dụng 2:

Tạo một mảng bằng chữ từ một iterable (thường là một chuỗi)

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

Trường hợp sử dụng 3:

Khai báo một số lượng đối số biến cho một hàm

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

Xem tài liệu mozilla


3
Bây giờ tôi có một downvote ở đây. Rõ ràng có ai đó đã ghi nhận một cái gì đó sai lầm khủng khiếp trong mẹo này, quá ngại để lại một bình luận và giải thích những gì ...
edc65

Có vẻ ổn. Có lẽ đó là thiếu dấu chấm phẩy? ;) (btw, bạn cũng có thể sử dụng nó làm tham số nghỉ ngơi, như biểu tượng trong Ruby)
gcampbell

Bạn có thể thêm rằng nó cũng có trường hợp sử dụng trong chữ ký hàm :)
Felix Dombek

Misclick không có nghĩa là downvote
Stan Strum

@StanStrum nó xảy ra. Tôi sẽ thực hiện một cập nhật nhỏ cho bài đăng này để cuối cùng bạn có thể thay đổi phiếu bầu của mình (hoặc bạn đã làm?)
edc65

21

Thủ thuật đã học ở đây kể từ khi tôi tham gia

Ngôn ngữ lập trình chính của tôi là JS và chủ yếu là ES6. Kể từ khi tôi tham gia trang web này một tuần trước, tôi đã học được rất nhiều thủ thuật hữu ích từ các thành viên đồng nghiệp. Tôi đang kết hợp một số trong số đó ở đây. Tất cả các khoản tín dụng cho cộng đồng.

Hàm mũi tên và vòng lặp

Chúng ta đều biết rằng các hàm mũi tên tiết kiệm rất nhiều byts

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

Nhưng bạn phải ghi nhớ một vài điều

  • Cố gắng câu lạc bộ nhiều câu lệnh bằng cách sử dụng ,tức là (a=b,a.map(d))- Ở đây, giá trị được trả về là biểu thức cuối cùnga.map(d)
  • nếu do somethingphần của bạn có nhiều hơn một câu lệnh, thì bạn cần thêm {}dấu ngoặc xung quanh .
  • Nếu có {}dấu ngoặc xung quanh , bạn cần thêm một câu trả lời rõ ràng.

Những điều trên đúng rất nhiều lần khi bạn có các vòng lặp liên quan. Vì vậy, một cái gì đó như:

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]}

Ở đây tôi đang lãng phí ít nhất 9 ký tự do sự trở lại. Điều này có thể được tối ưu hóa.

  • Cố gắng tránh các vòng lặp. Sử dụng .maphoặc .everyhoặc .somethay thế. Lưu ý rằng nếu bạn muốn thay đổi cùng một mảng mà bạn đang ánh xạ, nó sẽ thất bại.
  • Bọc vòng lặp trong hàm mũi tên đóng, chuyển đổi hàm mũi tên chính thành câu lệnh đơn.

Vì vậy, những điều trên trở thành:

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]

xóa các ký tự: {}return

thêm nhân vật: (){}>|

Lưu ý cách tôi gọi phương thức đóng, nó điền chính xác vào biến nvà sau đó vì phương thức đóng không trả lại bất cứ thứ gì (tức là trả về undefined), tôi bitwise hoặc nó và trả về mảng ntất cả trong một câu lệnh của hàm mũi tên ngoàiu

Dấu phẩy và dấu chấm phẩy

Tránh họ những gì đã từng,

Nếu bạn đang khai báo các biến trong một vòng lặp, hoặc như được đề cập trong phần trước, sử dụng ,các câu lệnh riêng biệt để có các hàm mũi tên câu lệnh đơn, thì bạn có thể sử dụng một số thủ thuật khá tiện lợi để tránh những điều đó ,hoặc ;để loại bỏ vài byte cuối cùng.

Hãy xem xét mã này:

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();....

Ở đây, tôi đang gọi rất nhiều phương thức để khởi tạo nhiều biến. Mỗi khởi tạo đang sử dụng một ,hoặc ;. Điều này có thể được viết lại như sau:

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());....

Lưu ý cách tôi sử dụng thực tế là phương thức không làm phiền biến được truyền cho nó và sử dụng thực tế đó để cạo 3 byte.

Linh tinh

.search thay vì .indexOf

Cả hai đều cho cùng một kết quả, nhưng searchngắn hơn. Mặc dù tìm kiếm mong đợi một Biểu thức chính quy, vì vậy hãy sử dụng nó một cách khôn ngoan.

`Chuỗi mẫu`

Đây là những siêu tiện dụng khi bạn phải nối một hoặc nhiều phần chuỗi dựa trên các điều kiện nhất định.

Lấy ví dụ sau để xuất ra một quine trong JS

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

so với

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

Trong một chuỗi mẫu, là một chuỗi bên trong hai backquote (`), mọi thứ bên trong a ${ }đều được coi là mã và được đánh giá để chèn câu trả lời kết quả vào chuỗi.

Tôi sẽ đăng thêm một vài thủ thuật sau. Chúc bạn chơi golf vui vẻ!


1
.search ngắn hơn, sử dụng nó khi có thể! nhưng nó không giống với .indexOf. .search muốn một regexp, không phải là một chuỗi. Hãy thử'abc'.search('.')
edc65

@ edc65 Cập nhật!
Tối ưu hóa

Bạn có thể sửa đổi mảng ban đầu với các phương thức thể hiện. Thứ hai là chỉ mục hiện tại và thứ ba là mảng được lặp lại.
Isiah Meadows

8
"Đã tham gia trang web một tuần trở lại" - Đại diện 21,4k ...
GamrCorps

2
Cũng như .map, đệ quy là một kỹ thuật khác đôi khi có thể giúp bạn biến một forvòng lặp thành một biểu thức.
Neil

20

Sử dụng tốc ký tài sản

Các tốc ký thuộc tính cho phép bạn đặt các biến thành các giá trị của mảng:

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

Điều này cũng có thể được sử dụng như:

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

Bạn thậm chí có thể sử dụng điều này để đảo ngược các biến:

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

Bạn cũng có thể sử dụng điều này để rút ngắn slice()chức năng.

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

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

Chuyển đổi cơ sở

ES6 cung cấp một cách ngắn hơn nhiều để chuyển đổi mẫu Base-2 (nhị phân) và Base-8 (bát phân) thành thập phân:

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

+có thể được sử dụng để chuyển đổi một chuỗi nhị phân, bát phân hoặc hex thành một số thập phân. Bạn có thể sử dụng 0b, 0o0x, tương ứng cho nhị phân, bát phân và hex.

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

Nếu bạn đang sử dụng cái này> 7 lần, thì nó sẽ ngắn hơn để sử dụng parseIntvà đổi tên nó:

(p=parseInt)(v,2)

Bây giờ pcó thể được sử dụng để parseInttiết kiệm cho bạn nhiều byte trong thời gian dài.


Thủ thuật chuyển đổi cơ sở là tốt, nhưng nhiều khả năng số chuyển đổi sẽ ở dạng biến thay vì theo nghĩa đen, trong trường hợp đó, nó sẽ trở nên dài hơn nhiều.
Trình tối ưu hóa

1
'0x'+v-0thậm chí còn ngắn hơn, nhưng có thể không hoạt động tốt trong một số tình huống.
Sản xuất ETH

1
Nhân tiện, 0767(ES5) ngắn hơn 0o767ký hiệu (ES6).
Camilo Martin

@CamiloMartin 0767là một tiện ích mở rộng không chuẩn và rõ ràng bị cấm ở chế độ nghiêm ngặt.
Oriol

1
@Oriol chế độ nghiêm ngặt là một meme xấu. Nó không giúp hiệu suất, không thực sự buộc bạn phải viết mã tốt và dù sao cũng sẽ không bao giờ trở thành mặc định. 0chữ bát phân -prefixed không đi bất cứ nơi nào, và là bản mô tả hợp lệ như 0o.
Camilo Martin

19

Sử dụng các mẫu chuỗi có chức năng

Khi bạn có một hàm với một chuỗi làm đối số. Bạn có thể bỏ qua ()nếu bạn không có bất kỳ biểu thức nào:

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

9
Được cảnh báo, điều này thực sự vượt qua một mảng. fun`string` là giống như fun(["string"]), không fun("string"). Điều này tốt cho các hàm truyền thành chuỗi, như alert, nhưng đối với các hàm khác, điều này có thể gây ra sự cố. Để biết thêm thông tin, hãy xem bài viết MDN
Cyoce

5
Điều tham khảo nhanh: fun`foo${1}bar${2}baztương đương với việc gọi điện thoạifun(["foo","bar","baz"],1,2)
Cyoce

14

Hiểu mảng (Firefox 30-57)

Lưu ý: việc hiểu mảng không bao giờ được chuẩn hóa và đã bị lỗi thời với Firefox 58. Sử dụng một cách nguy hiểm.


Ban đầu, thông số ECMAScript 7 chứa một loạt các tính năng dựa trên mảng mới. Mặc dù hầu hết trong số này không biến nó thành phiên bản hoàn thiện, nhưng hỗ trợ Firefox (ed) có thể là tính năng lớn nhất trong số các tính năng này: cú pháp mới lạ mắt có thể thay thế .filter.mapbằng for(a of b)cú pháp. Đây là một ví dụ:

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

Như bạn có thể thấy, hai dòng này không khác nhau nhiều, ngoại trừ dòng thứ hai không chứa các từ khóa cồng kềnh và các hàm mũi tên. Nhưng điều này chỉ chiếm cho đơn đặt hàng .filter().map(); Điều gì xảy ra nếu bạn có .map().filter()thay thế? Nó thực sự phụ thuộc vào tình hình:

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]

Hoặc nếu bạn muốn một trong hai .map hoặc .filter ? Vâng, nó thường hóa ra ít 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]

Vì vậy, lời khuyên của tôi là sử dụng mảng hiểu bất cứ nơi nào bạn thường sử dụng .map .filter , nhưng không chỉ cái này hay cái khác.

Chuỗi hiểu

Một điều tuyệt vời về khả năng hiểu ES7 là, không giống như các hàm dành riêng cho mảng như .map.filter, chúng có thể được sử dụng trên bất kỳ đối tượng lặp nào , không chỉ các mảng. Điều này đặc biệt hữu ích khi xử lý chuỗi. Ví dụ: nếu bạn muốn chạy từng ký tự ctrong một chuỗi thông qua c.charCodeAt():

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

Đó là hai byte được lưu ở quy mô khá nhỏ. Và nếu bạn muốn lọc một số ký tự trong một chuỗi thì sao? Ví dụ: cái này chỉ giữ chữ in hoa:

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

Hmm, không ngắn hơn. Nhưng nếu chúng ta kết hợp cả hai:

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

Wow, toàn bộ 10 byte được lưu!

Một ưu điểm khác của việc hiểu chuỗi là các chuỗi được mã hóa cứng tiết kiệm thêm một byte, vì bạn có thể bỏ qua khoảng trắng sau 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]

Lập chỉ mục

Việc hiểu mảng làm cho việc lấy chỉ mục hiện tại trong chuỗi / mảng khó hơn một chút, nhưng có thể được thực hiện:

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

Điều chính cần cẩn thận là đảm bảo chỉ số được tăng lên mỗi lần, không chỉ khi một điều kiện được đáp ứng.

Hiểu máy phát điện

Hiểu máy phát điện về cơ bản có cùng cú pháp như hiểu mảng; chỉ cần thay thế dấu ngoặc bằng dấu ngoặc đơn:

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

Điều này tạo ra một trình tạo có chức năng giống như một mảng, nhưng đó là một câu chuyện cho một câu trả lời khác.

Tóm lược

Về cơ bản, mặc dù sự hiểu biết thường ngắn hơn .map().filter(), nhưng tất cả đều đi vào chi tiết cụ thể của tình huống. Tốt nhất là thử cả hai cách và xem cách nào hiệu quả hơn.

PS Hãy thoải mái đề xuất một mẹo liên quan đến sự hiểu biết khác hoặc một cách để cải thiện câu trả lời này!


Đây là một mẹo cho các phạm vi sẽ lưu thêm một vài ký tự:(x,y)=>[...Array(y-x)].map(a=>x++)
Mwr247

2
Bạn có thể cắt thêm 11 byte để tạo phạm vi từ 0 đến x:x=>[...Array(x).keys()]
Mwr247

n=>[for(x of Array(n).keys())if(/1/.test(x))x]
Cái

@ Mwr247 Trên thực tế, tôi có thể thấy rằng các phạm vi thường không ngắn bằng các hiểu biết như với các tính năng ES6 đẹp khác. Thay vào đó, tôi sẽ thêm một phần vào chuỗi và cho phép bạn xử lý các phạm vi.
Sản xuất ETH

Điều đáng chú ý là Array Comp Hiểu đã bị phản đối và bị xóa khỏi tất cả các phiên bản javascript gần đây. Xem tài liệu MDN về chủ đề này.
Keefer Rourke

13

Các biểu thức hàm trong ES6 sử dụng ký hiệu mũi tên và nó giúp tiết kiệm rất nhiều byte nếu so với phiên bản ES5:

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

Nếu hàm của bạn chỉ có một tham số, bạn có thể bỏ qua dấu ngoặc đơn để lưu hai byte:

f=x=>x+1

Nếu hàm của bạn không có tham số nào cả, hãy khai báo nó như thể nó có một để lưu một byte:

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

Cẩn thận: Các hàm mũi tên không hoàn toàn giống như function () {}. Các quy tắc thislà khác nhau (và IMO tốt hơn). Xem tài liệu


2
Nhưng khi bạn chơi gôn, bạn thường không quan tâm, thisv.v.
Trình tối ưu hóa

1
Nói chung là không, nhưng đó là một cảnh báo, một điều mà bạn có thể không bao giờ biết khi nó xuất hiện. Nó cũng phổ biến hơn cho lambdas không cần một chức năng - địa phương ràng buộc này trong sản xuất.
Isiah Meadows

Ngoài ra, nếu bạn muốn lấy tất cả các đối số của mình, bạn có thể sử dụng tính năng đối số "phần còn lại", ví dụ: f=(...x)=>x sẽ có điều đó f(1,2,3) => [1,2,3].
Conor O'Brien

1
Đây là một mẹo cụ thể cho trang web này: nếu bạn đang trả lời bằng một hàm có dạng (x,y)=>...bạn có thể lưu một byte bằng cách sử dụng currying bằng cách thay thế nó bằngx=>y=>...
Cyoce

12

Sử dụng evalcho các hàm mũi tên với nhiều câu lệnh và mộtreturn

Một trong những mánh khóe lố bịch hơn mà tôi vấp phải ...

Hãy tưởng tượng một hàm mũi tên đơn giản cần nhiều câu lệnh và a return.

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

Một hàm đơn giản chấp nhận một tham số duy nhất a, lặp lại trên tất cả các số nguyên trong [0, a)và xử lý chúng vào cuối chuỗi đầu ra o, được trả về. Ví dụ, gọi đây 4là tham số sẽ mang lại 0123.

Lưu ý rằng chức năng mũi tên này phải được bọc trong dấu ngoặc nhọn {}và có một dấu return oở cuối.

Nỗ lực đầu tiên này nặng tới 39 byte .

Không tệ, nhưng bằng cách sử dụng eval, chúng ta có thể cải thiện điều này.

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

Hàm này đã loại bỏ các dấu ngoặc nhọn và câu lệnh return bằng cách gói mã trong một evalvà chỉ cần thực hiện câu lệnh cuối cùng trong evalđánh giá o. Điều này gây ra evaltrả về o, do đó làm cho hàm trả về o, vì bây giờ nó là một câu lệnh đơn.

Nỗ lực cải tiến này nặng tới 38 byte , tiết kiệm một byte so với ban đầu.

Nhưng xin chờ chút nữa! Tuyên bố Eval trả lại bất cứ điều gì tuyên bố cuối cùng của họ được đánh giá. Trong trường hợp này, o+=iđánh giá o, vì vậy chúng tôi không cần ;o! (Cảm ơn, edc65!)

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

Lần thử cuối cùng này chỉ nặng 36 byte - tiết kiệm 3 byte so với ban đầu!


Kỹ thuật này có thể được mở rộng cho bất kỳ trường hợp chung nào khi hàm mũi tên cần trả về giá trị và có nhiều câu lệnh (không thể kết hợp bằng các phương tiện khác)

b=>{statement1;statement2;return v}

trở thành

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

tiết kiệm một byte.

Nếu statement2đánh giá v, điều này có thể

b=>eval('statement1;statement2')

tiết kiệm tổng cộng 3 byte.


1
Tôi nghĩ rằng, chỉ cần viết một chức năng ẩn danh có thể còn ngắn hơn
Downgoat

@vihan yeah, cả hai hàm này đều có thể được ẩn danh để lưu 2 byte mỗi hàm. Tiết kiệm một byte vẫn đứng mặc dù.
jrich

1
Nhưng thậm chí còn tốt hơn: eval trả lại biểu thức cuối cùng được đánh giá, vì vậy bạn không cần ;o- hãy thử:a=>eval('for(o="",i=0;i<a;i++)o+=i')
edc65

4
Nhưng chuỗi mẫu!
Conor O'Brien

1
@ CᴏɴᴏʀO'Bʀɪᴇɴ Muốn giải thích cách các chuỗi mẫu sẽ hoạt động ở đây bằng cách sử dụng hàm ví dụ làm bối cảnh?
WallyWest

10

Thích chuỗi mẫu mới hơn "\ n"

Điều này sẽ bắt đầu trả hết ngay cả một ký tự dòng mới trong mã của bạn. Một trường hợp sử dụng có thể là:

(16 byte)

array.join("\n")

(15 byte)

array.join(`
`)

Cập nhật: Bạn thậm chí có thể bỏ đi các dấu ngoặc do các chuỗi mẫu được gắn thẻ (cảm ơn, edc65!):

(13 byte)

array.join`
`

5
Nhưng thậm chí tốt hơn, bạn có thể tránh dấu ngoặc đơn. Đọc tài liệu ( developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/
mẹo

À, đúng rồi. Cảm ơn, tôi đã thêm nó.
Chiru

9

Mảng đầy - Giá trị tĩnh & Phạm vi động

Ban đầu tôi đã để lại những nhận xét này dưới sự hiểu biết, nhưng vì bài đăng đó chủ yếu tập trung vào sự hiểu biết, tôi nhận ra rằng sẽ rất tốt nếu đặt nó ở vị trí riêng của nó.

ES6 đã cho chúng tôi khả năng lấp đầy các mảng bằng các giá trị tĩnh mà không cần sử dụng các vòng lặp:

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

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

Cả hai đều trả về một mảng có độ dài x, chứa đầy giá trị 0.

Tuy nhiên, nếu bạn muốn điền vào các mảng bằng các giá trị động (chẳng hạn như phạm vi từ 0 ... x), kết quả sẽ dài hơn một chút (mặc dù vẫn ngắn hơn cách cũ):

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

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

Cả hai đều trả về một mảng có độ dài x, bắt đầu bằng giá trị 0 và kết thúc bằng x-1.

Lý do bạn cần .fill()có là vì chỉ cần khởi tạo một mảng sẽ không cho phép bạn ánh xạ nó. Đó là để nói, làm x=>Array(x).map((a,i)=>i)sẽ trả về một mảng trống. Bạn cũng có thể giải quyết nhu cầu điền (và do đó làm cho nó ngắn hơn nữa) bằng cách sử dụng toán tử trải rộng như vậy:

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

Sử dụng toán tử trải rộng và .keys()hàm, giờ đây bạn có thể tạo một phạm vi 0 ... x ngắn:

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

Nếu bạn muốn một phạm vi tùy chỉnh từ x ... y hoặc một phạm vi chuyên biệt hoàn toàn (chẳng hạn như số chẵn), bạn có thể thoát khỏi .keys()và chỉ sử dụng .map()hoặc sử dụng .filter()với toán tử trải rộng:

// 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)

Đây là một gợi ý cho ví dụ thứ hai: x=>Array(x).fill(i=0).map(a=>i++)Ngoài ra, tôi không chắc chắn rằng 0 trong .fill(0)là cần thiết. Bạn đã thử nó mà không?
Sản xuất ETH

@ETHproductions Bạn nói đúng, tôi quên 0 không cần thiết trong phần điền trước bản đồ. Điều này làm cho nó ngắn hơn 1 ký tự so với đề xuất của bạn, vì vậy tôi sẽ giữ nó như thế. Cảm ơn!
Mwr247

Ngoài ra, đối với ví dụ cuối cùng, a=>a%2-1hoạt động tốt, cũng như a=>a%2<1.
Sản xuất ETH

1
Một mẹo mới mà tôi đã học được: [...Array(x)]hoạt động tốt như vậy Array(x).fill(), và ngắn hơn 2 byte. x=>[...Array(x)].map((a,i)=>i)
Sản xuất ETH

1
@yonatanmn Rất đẹp! Chỉ nhận xét là 1) 1/4ví dụ sẽ được viết ngắn hơn [0,0,0,0]và 2) các hàm được sắp xếp theo chuỗi cụ thể, do đó sẽ không trả về độ dài đáng tin cậy ( Maplà 32 byte trong Chrome, nhưng 36 byte trong Firefox).
Mwr247

9

Trả về các giá trị trong các hàm mũi tên

Kiến thức phổ biến là nếu một câu lệnh duy nhất tuân theo khai báo hàm mũi tên, nó sẽ trả về kết quả của câu lệnh đó:

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

-7 byte

Vì vậy, khi có thể, kết hợp nhiều câu lệnh thành một. Điều này được thực hiện dễ dàng nhất bằng cách bao quanh các câu lệnh bằng dấu ngoặc đơn và phân tách chúng bằng dấu phẩy:

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

-8 byte

Nhưng nếu chỉ có hai câu lệnh, thường có thể (và ngắn hơn) để kết hợp chúng với &&hoặc ||:

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 byte

Cuối cùng, nếu bạn đang sử dụng bản đồ (hoặc tương tự) và cần trả về một số và bạn có thể đảm bảo bản đồ sẽ không bao giờ trả về một mảng có độ dài bằng một số, bạn có thể trả về số đó bằng |:

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

Trong ví dụ cuối cùng đó, bạn cũng cần chắc chắn rằng số đó sẽ luôn là một số nguyên.
Sản phẩm ETH

8

Hacks mẫu chuỗi ngẫu nhiên

Hàm này kiểm tra hai chuỗi (tức là biến "abc","de"thành "adbec"):

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

Lưu ý rằng điều này chỉ hoạt động khi xdài hơn y. bạn hỏi nó thực hiện như thế nào ư? String.rawđược thiết kế để trở thành một thẻ mẫu, như vậy:

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

Điều này về cơ bản gọi String.raw(["x: ", "\ny: ", "\nx + y: ", ""], x, y, x + y), mặc dù nó không đơn giản. Mảng mẫu cũng có một thuộc tính đặc biệt raw, về cơ bản là một bản sao của mảng, nhưng với các chuỗi thô. String.raw(x, ...args)về cơ bản trả về x.raw[0] + args[0] + x.raw[1] + args[1] + x.raw[2] + ...và cứ như vậy cho đến khi xhết hàng.

Vì vậy, bây giờ chúng tôi biết làm thế nào String.rawhoạt động, chúng tôi có thể sử dụng nó để lợi thế của chúng tôi:

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"

Tất nhiên, đối với cái cuối cùng, f=(x,y)=>x.split``.join(y)là cách ngắn hơn, nhưng bạn có ý tưởng.

Dưới đây là một số hàm riffling cũng hoạt động nếu xycó độ dài bằng nhau:

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

Bạn có thể tìm hiểu thêm về String.raw MDN .


7

Làm thế nào để chơi golf với đệ quy

Đệ quy, mặc dù không phải là lựa chọn nhanh nhất, nhưng thường là ngắn nhất. Nói chung, đệ quy là ngắn nhất nếu giải pháp có thể đơn giản hóa giải pháp thành một phần nhỏ hơn của thách thức, đặc biệt nếu đầu vào là một số hoặc một chuỗi. Ví dụ, nếu f("abcd")có thể được tính từ "a"f("bcd"), tốt nhất nên sử dụng đệ quy.

Lấy ví dụ, giai thừa:

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

Trong ví dụ này, đệ quy rõ ràng là ngắn hơn bất kỳ tùy chọn nào khác.

Làm thế nào về tổng số mã hóa:

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

Điều này là khó khăn hơn, nhưng chúng ta có thể thấy rằng khi được thực hiện chính xác, đệ quy sẽ tiết kiệm được 4 byte .map.

Bây giờ chúng ta hãy xem các loại đệ quy khác nhau:

Tiền đệ quy

Đây thường là loại đệ quy ngắn nhất. Các đầu vào được chia thành hai phần ab, và các chức năng tính toán cái gì đó với af(b). Quay trở lại ví dụ giai thừa của chúng tôi:

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

Trong trường hợp này, an , bn-1 và giá trị được trả về là a*f(b).

Lưu ý quan trọng: Tất cả các hàm đệ quy phải có cách dừng đệ quy khi đầu vào đủ nhỏ. Trong chức năng giai thừa, điều này được điều khiển bằng n? :1, tức là nếu đầu vào bằng 0 , trả về 1 mà không gọi flại.

Hậu đệ quy

Hậu đệ tương tự như đệ quy trước, nhưng hơi khác. Đầu vào được chia thành hai phần ab, và hàm tính toán một cái gì đó với a, sau đó gọi f(b,a). Đối số thứ hai thường có giá trị mặc định (nghĩa làf(a,b=1) ).

Tiền đệ quy là tốt khi bạn cần làm một cái gì đó đặc biệt với kết quả cuối cùng. Ví dụ: nếu bạn muốn giai thừa của một số cộng 1:

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

Tuy nhiên, ngay cả sau đó, không phải lúc nào cũng ngắn hơn sử dụng đệ quy trước trong một chức năng khác:

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

Vậy khi nào nó ngắn hơn? Bạn có thể nhận thấy rằng đệ quy sau trong ví dụ này yêu cầu dấu ngoặc đơn xung quanh các đối số hàm, trong khi đệ quy trước thì không. Nói chung, nếu cả hai giải pháp đều cần dấu ngoặc đơn xung quanh các đối số, thì đệ quy sau ngắn hơn khoảng 2 byte:

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

(các chương trình ở đây được lấy từ câu trả lời này )

Làm thế nào để tìm ra giải pháp ngắn nhất

Thông thường cách duy nhất để tìm ra phương pháp ngắn nhất là thử tất cả chúng. Điêu nay bao gôm:

  • Vòng lặp
  • .map(đối với các chuỗi, một trong hai [...s].maphoặc s.replace, vì con số, bạn có thể tạo ra một phạm vi )
  • Hiểu mảng
  • Tiền đệ quy (đôi khi trong các tùy chọn khác)
  • Hậu đệ quy

Và đây chỉ là những giải pháp phổ biến nhất; giải pháp tốt nhất có thể là sự kết hợp của những điều này, hoặc thậm chí là một cái gì đó hoàn toàn khác . Cách tốt nhất để tìm ra giải pháp ngắn nhất là thử mọi thứ .


1
+1 cho giá trị của nó và tôi muốn thêm +1 cho zootopia
edc65

7

Cách ngắn hơn để làm .replace


Nếu bạn muốn thay thế tất cả các phiên bản của một chuỗi con chính xác bằng một chuỗi khác trong một chuỗi, cách rõ ràng sẽ là:

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

Tuy nhiên, bạn có thể thực hiện ngắn hơn 1 byte:

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

Lưu ý rằng điều này không còn ngắn hơn nếu bạn muốn sử dụng bất kỳ tính năng regex nào ngoài gcờ. Tuy nhiên, nếu bạn thay thế tất cả các phiên bản của một biến, nó thường ngắn hơn nhiều:

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!"

Đôi khi, bạn sẽ muốn ánh xạ qua từng char trong một chuỗi, thay thế từng chuỗi bằng một thứ khác. Tôi thường thấy mình làm điều này:

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

Tuy nhiên, .replacehầu như luôn luôn ngắn hơn:

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

Bây giờ, nếu bạn muốn ánh xạ qua từng char trong một chuỗi nhưng không quan tâm đến chuỗi kết quả, .mapthường sẽ tốt hơn vì bạn có thể thoát khỏi .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

Đối với trường hợp cuối cùng, nếu chỉ có một số ký tự phù hợp với biểu thức chính quy (thích /\w/g), thì việc sử dụng thay thế sẽ tốt hơn nhiều như trong bản demo này .
Shieru Asakoto

6

Viết chữ RegEx với eval

Hàm tạo regex có thể rất cồng kềnh do tên dài của nó. Thay vào đó, hãy viết một chữ với eval và backticks:

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

Nếu biến ibằng foo, điều này sẽ tạo ra:

/<foo [^>]+/g

Điều này tương đương với:

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

Bạn cũng có thể sử dụng String.rawđể tránh phải liên tục thoát khỏi dấu gạch chéo ngược\

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

Điều này sẽ xuất ra:

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

Điều này tương đương với:

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

Ghi nhớ!

String.rawchiếm rất nhiều byte và trừ khi bạn có ít nhất chín dấu gạch chéo ngược, String.rawsẽ dài hơn.


Bạn không cần newtrong đó, vì vậy việc sử dụng hàm tạo thực sự ngắn hơn cho ví dụ thứ hai
Trình tối ưu hóa

5

.forEachvs forvòng

Luôn luôn thích .mapbất kỳ vòng lặp cho. Dễ dàng, tiết kiệm ngay lập tức.


a.map(f)
for(x of a)f(x);
for(i=0;i<a.length;)f(a[i++]);
  • Tổng cộng 8 byte cho bản gốc
  • 8 byte được lưu so với for-of ( giảm 50% )
  • 22 byte được lưu so với kiểu C cho vòng lặp ( giảm 73% )

a.map(x=>f(x,0))
for(x of a)f(x,0);
for(i=0;i<a.length;)f(a[i++],0);
  • Tổng cộng 16 byte cho bản gốc
  • 2 byte được lưu so với for-of ( giảm 11% )
  • 16 byte được lưu so với kiểu C cho vòng lặp ( giảm 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);
  • Tổng cộng 22 byte cho bản gốc
  • Lưu 1 byte so với for-in ( 4% giảm )
  • 11 byte được lưu so với kiểu C cho vòng lặp ( giảm 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);
  • Tổng cộng 19 byte cho bản gốc
  • 2 byte được lưu so với for-of ( 10% giảm )
  • 18 byte được lưu so với kiểu C cho vòng lặp ( giảm 49% )

5

Sử dụng bộ đếm chưa được khởi tạo trong đệ quy

Lưu ý : Nói đúng ra, điều này không đặc trưng cho ES6. Tuy nhiên, việc sử dụng và lạm dụng đệ quy trong ES6 có ý nghĩa hơn vì tính chất ngắn gọn của các hàm mũi tên.


Điều khá phổ biến là bắt gặp một hàm đệ quy sử dụng bộ đếm kban đầu được đặt thành 0 và tăng dần ở mỗi lần lặp:

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

Trong một số trường hợp nhất định, có thể bỏ qua việc khởi tạo bộ đếm như vậy và thay thế k+1bằng -~k:

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

Thủ thuật này thường tiết kiệm 2 byte .

Tại sao và khi nào nó hoạt động?

Công thức làm cho nó có thể là ~undefined === -1. Vì vậy, ở lần lặp đầu tiên, -~ksẽ được đánh giá 1. Trên lặp tiếp theo, -~kvề cơ bản là tương đương với -(-k-1)bằng với k+1, ít nhất là cho số nguyên trong khoảng [0 ... 2 31 -1].

Tuy nhiên, bạn phải đảm bảo rằng việc k = undefinedlặp lại lần đầu tiên sẽ không phá vỡ hành vi của hàm. Bạn đặc biệt nên nhớ rằng hầu hết các phép toán số học liên quan undefinedsẽ dẫn đếnNaN .

Ví dụ 1

Cho một số nguyên dương n, hàm này tìm số nguyên nhỏ nhất kkhông chia n:

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

Nó có thể được rút ngắn thành:

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

Này hoạt động vì n % undefinedNaN, đó là falsy. Đó là kết quả mong đợi ở lần lặp đầu tiên.

[Liên kết đến câu trả lời gốc]

Ví dụ # 2

Cho một số nguyên dương n, hàm này tìm một số nguyên psao cho (3**p) - 1 == n:

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

Nó có thể được rút ngắn thành:

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

Điều này hoạt động bởi vì hoàn toàn pkhông được sử dụng trong lần lặp đầu tiên ( n<klà sai).

[Liên kết đến câu trả lời gốc]


5

Chức năng ES6

môn Toán

Math.cbrt(x)tiết kiệm nhân vật hơn Math.pow(x,1/3).

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

Lưu 3 ký tự

Math.hypot(...args)là hữu ích khi bạn cần căn bậc hai của tổng bình phương của các đối số. Tạo mã ES5 để làm điều đó khó hơn nhiều so với sử dụng tích hợp.

Các chức năng Math.trunc(x)sẽ không hữu ích, như x|0là ngắn hơn. (Cảm ơn Mwr247!)

Có nhiều thuộc tính cần nhiều mã để làm trong ES5, nhưng dễ hơn trong ES6:

  • Math.acosh, asinh, atanh, cosh, sinh, tanh. Tính toán tương đương hyperbolic của các hàm lượng giác.
  • Math.clz32. Có thể làm trong ES5, nhưng giờ thì dễ hơn. Đếm các số 0 đứng đầu trong biểu diễn 32 bit của một số.

Có rất nhiều hơn, vì vậy tôi chỉ cần đi để liệt kê một số:
Math.sign, Math.fround, Math.imul, Math.log10, Math.log2, Math.log1p.


Math.trunc(x)dài hơn bốn lần so với x|0.
Mwr247

@ mwr247: Ok, sẽ cập nhật.
ev3commander

Dưới đây là các khoản tương đương ES5 ngắn nhất tôi biết cho một vài các chức năng: Math.hypot(a,b) => Math.sqrt(a*a+b*b)(3 byte còn; được thậm chí lâu hơn với các đối số hơn), Math.sign(a) => (a>0)-(a<0)(1 byte ngắn hơn, nhưng nhu cầu ngoặc xung quanh trong một số trường hợp; có thể không làm việc với NaN)
ETHproductions

@ETHproductions Bạn cần mảng đối số cho hypotound của es5. Và bạn có chắc rằng cách giải quyết cho Math.sign hoạt động cho -0 không? (Nó sẽ trả về -0)
ev3commander

1
@ ev3commander Đây chỉ là các thay thế nội dòng cho các tương đương ES6 tương ứng của chúng, vì vậy chúng được thu nhỏ lại cho 99% sử dụng. Thực sự tái tạo các chức năng này sẽ đòi hỏi nhiều mã hơn. Ngoài ra, tôi thấy không có lý do gì để cần phải có một trường hợp đặc biệt cho -0, vì (AFAIK) không có cách nào để có được -0 ngoại trừ bằng cách chỉ định thủ công và thực tế không sử dụng nó trong môn đánh gôn. Nhưng cảm ơn vì đã chỉ ra những điều đó.
Sản xuất ETH

5

Tối ưu hóa phạm vi không đổi nhỏ cho map()

Bối cảnh

map()for[0 ..VIẾT SAI RỒI-1]

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

có thể được thay thế bằng một trong hai:

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

hoặc phổ biến hơn:

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

Array(N)VIẾT SAI RỒI là một hằng số nhỏ.

[0 ..VIẾT SAI RỒI-1] , với bộ đếm

Tô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 : Độ dài của mã gọi lại F(i)không được tính.

[1..9]

[1..9]

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

Tối ưu hóa mà không cần truy cập

VIẾT SAI RỒI

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 : Độ dài của mã gọi lại F()không được tính.


Không nên 2**262**29?
Xù xì

@Shaggy Heck. Nắm bắt tốt!
Arnauld

Không muốn tự mình chỉnh sửa vì tôi bị mù mã! : D
Xù xì

Sử dụng .keys(), bạn không cần lambda:[...Array(10).keys()].map(do_something_with)
long-lazuli

@ long-lazuli Nếu bạn không cần lambda và chỉ muốn một phạm vi, thì có lẽ bạn cũng không cần bản đồ ...
Arnauld

4

Phân công nhiệm vụ

ES6 giới thiệu cú pháp mới để hủy các bài tập, nghĩa là cắt một giá trị thành các phần và gán mỗi phần cho một biến khác nhau. Đây là vài ví dụ:

Chuỗi và mảng

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

Các đối tượng

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

Các bài tập này cũng có thể được sử dụng trong các tham số chức năng:

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

Một cách khác để tránh return

Bạn biết bạn nên sử dụng eval cho các hàm mũi tên với nhiều câu lệnh và trả về . Trong một số trường hợp bất thường, bạn có thể tiết kiệm nhiều hơn bằng cách sử dụng một chức năng con bên trong.

Tôi nói khác thường

  1. Kết quả trả về không được là biểu thức cuối cùng được đánh giá trong vòng lặp

  2. Phải có (ít nhất) 2 lần khởi tạo khác nhau trước vòng lặp

Trong trường hợp này, bạn có thể sử dụng một hàm con bên trong mà không trả về, có một trong các giá trị ban đầu được truyền dưới dạng tham số.

Thí dụ Tìm đối ứng của tổng hàm exp cho các giá trị trong phạm vi từ a đến b.

Con đường dài - 55 byte

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

Với eval - 54 byte

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

Với chức năng bên trong - 53 byte

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

Lưu ý rằng không có yêu cầu giới hạn phạm vi thấp hơn a, tôi có thể hợp nhất các khởi tạo của i và r và phiên bản eval ngắn hơn.


Trong mẫu của bạn không cần phải giữa
l4m2

@ l4m2 Tôi không thể biết quan điểm của bạn, xin vui lòng giúp đỡ ...
edc65

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

@ l4m2 uh đúng, return a/rsẽ là một ví dụ tốt hơn
edc65

1
eval vẫn tốt hơn (a,b)=>1/eval("for(r=0,i=a;i<=b;i++)r+=Math.exp(i)")và trong trường hợp này(i,b)=>1/eval("for(r=0;i<=b;)r+=Math.exp(i++)")
JayXon

4

Sử dụng cú pháp currying cho các hàm dyadic và đệ quy

Chức năng Dyadic

Bất cứ khi nào một hàm có chính xác hai đối số không có giá trị mặc định, sử dụng cú pháp currying sẽ lưu một byte.

Trước

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

Được gọi với f(a,b)

Sau

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

Được gọi với f(a)(b)

Lưu ý : Bài đăng này trong Meta xác nhận tính hợp lệ của cú pháp này.

Hàm đệ quy

Sử dụng cú pháp currying cũng có thể lưu một số byte khi một hàm đệ quy có một vài đối số nhưng chỉ cần cập nhật một số trong số chúng giữa mỗi lần lặp.

Thí dụ

Hàm sau tính tổng của tất cả các số nguyên trong phạm vi [a,b]:

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

akhông thay đổi trong toàn bộ quá trình, chúng tôi có thể lưu 3 byte bằng cách sử dụng:

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

Lưu ý : Như Neil nhận thấy trong các bình luận, việc một đối số không được chuyển rõ ràng đến hàm đệ quy không có nghĩa là nó nên được coi là bất biến. Nếu cần thiết, chúng ta có thể thay đổi atrong mã chức năng với a++, a--hoặc bất cứ cú pháp tương tự.


Ví dụ cuối cùng có thể được viết dưới dạng a=>F=b=>a>b?0:a+++F(b), sửa đổi acho mỗi cuộc gọi đệ quy. Điều này không giúp ích gì trong trường hợp đó nhưng nó có thể lưu byte trong trường hợp có nhiều đối số hơn.
Neil

Heh, tôi chỉ nghĩ về việc viết một mẹo cho việc này :-)
Sản phẩm ETH

4

Chức năng kiểm tra tính nguyên thủy

Hàm 28 byte sau đây trả về truecác số nguyên tố và falsecho các số nguyên tố không:

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

Điều này có thể dễ dàng được sửa đổi để tính toán những thứ khác. Ví dụ, hàm 39 byte này đếm số lượng các số nguyên tố nhỏ hơn hoặc bằng một số:

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

Nếu bạn đã có một biến nmà bạn muốn kiểm tra tính nguyên thủy, hàm nguyên thủy có thể được đơn giản hóa một chút:

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

Làm thế nào nó hoạt động

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.

Lưu ý: Điều này sẽ thất bại với lỗi "quá nhiều đệ quy" khi được gọi với đầu vào đủ lớn, chẳng hạn như 12345. Bạn có thể khắc phục điều này bằng một vòng lặp:

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

1
Nhưng thất bại với quá nhiều đệ quy cho đầu vào ít nhất là 12345
edc65

x==1có thể là x<2để tiết kiệm
Máy

@CalculatorFeline Cảm ơn, nhưng sau đó nó không thành công 1hoặc 0(vì xsẽ tương ứng 0hoặc -1)
ETHproductions 20/07/17

Có thể hữu ích trong một số trường hợp. Ngoài ra, !~-xvới -0 byte.
Máy tính dòng

3

Array#concat() và toán tử lây lan

Điều này phần lớn phụ thuộc vào tình hình.


Kết hợp nhiều mảng.

Thích chức năng concat trừ khi nhân bản.

Đã lưu 0 byte

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

Lãng phí 3 byte

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

Đã lưu 3 byte

a.concat()
[...a]

Đã lưu 6 byte

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

Thích sử dụng một mảng đã có sẵn để Array#concat().

Dễ dàng lưu 4 byte

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

3

Trả về kết quả trung gian

Bạn biết rằng bằng cách sử dụng toán tử dấu phẩy, bạn có thể thực hiện một chuỗi các biểu thức trả về giá trị cuối cùng. Nhưng lạm dụng cú pháp mảng bằng chữ, bạn có thể trả về bất kỳ giá trị trung gian nào. Nó hữu ích trong .map () chẳng hạn.

// 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
Tất nhiên, hãy nhớ rằng đó .join('')có thể là.join``
Cyoce

3

Đặt mặc định tham số chức năng

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

Điều này thực sự hữu ích ...

Tuy nhiên, hãy chắc chắn hiểu rằng một cái gì đó giống như _=>_||'asdf'ngắn hơn khi bạn chỉ truyền một đối số (hữu ích) cho hàm.


1
Tôi lưu ý rằng việc sử dụng OR _=>_||'asdf'thường ngắn hơn trong hầu hết các trường hợp
Downgoat

@Downgoat Tôi lưu ý rằng trả về "asdf"cho đầu vào của ""(chuỗi trống).
Sản xuất ETH

2
Lưu ý rằng mặc định được đánh giá bất cứ khi nào đối số sẽ có undefined, ngay cả khi bạn rõ ràng vượt qua giá trị đó. Ví dụ: [...Array(n)].map((a,b,c)=>b)luôn luôn chuyển undefinedcho avà do đó bạn có thể cung cấp một giá trị mặc định cho nó (mặc dù không phải về mặt b).
Neil

3

Sử dụng evalthay vì niềng răng cho các chức năng mũi tên

Chức năng mũi tên là tuyệt vời. Chúng có dạng x=>y, nơi xlà một đối số và ylà giá trị trả về. Tuy nhiên, nếu bạn cần sử dụng cấu trúc điều khiển, chẳng hạn như while, bạn sẽ phải đặt dấu ngoặc nhọn, vd =>{while(){};return}. Tuy nhiên, chúng ta có thể vượt qua điều này; may mắn thay, evalhàm lấy một chuỗi, đánh giá chuỗi đó dưới dạng mã JS và trả về biểu thức được đánh giá cuối cùng . Ví dụ, so sánh hai:

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

Chúng ta có thể sử dụng một phần mở rộng của khái niệm này để rút ngắn hơn nữa mã của chúng tôi: trong mắt eval, các cấu trúc điều khiển cũng trả về biểu thức được đánh giá cuối cùng của chúng. Ví dụ:

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

3

Hoạt động logic chơi gôn trong ES6

"GLOE (S6)"

Logic chung

Giả sử bạn đã xây dựng báo cáo st. Xem nếu bạn có thể sử dụng bất kỳ thay thế nào sau đây:

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

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

(Những thứ này có thể không hoạt động nếu thứ tự sai; +*có một ưu tiên theo thứ tự thấp hơn ||&&làm.)

Ngoài ra, đây là một số biểu thức logic tiện dụng:

  • Hoặc s hay tlà đúng / XOR:s^t
  • st có cùng giá trị sự thật: !s^thoặcs==t

Mảng logic

Tất cả các thành viên của ađiều kiện đáp ứngp :

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)

Ít nhất một thành viên của ađiều kiện thỏa mãnp :

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)

Không có thành viên của a điều kiện thỏa mãn p: !a.some(p).

Phần tử etồn tại trong mảnga :

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

Yếu tố enào không tồn tại trong mảng 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++]);

Tôi thường sử dụng &&||như x?y:xx?x:y, tương ứng. Nhưng tôi có thể thấy làm thế nào điều này sẽ hữu ích trong các chương trình dựa trên logic hơn. Một vấn đề với +đó là ví dụ 3-3cả hai đều đúng, nhưng 3+-3không phải.
Sản phẩm điện tử

@ETHproductions Ah, bạn nói đúng; đó là một trường hợp cạnh. -cũng có thể làm việc, nếu s != t.
Conor O'Brien

a.filter(t=>t==e).length==a.lengthkhông chính xác Nó nên là!a.filter(t=>t==e).length
ETHproductions

@ETHproductions đúng là bạn!
Conor O'Brien

3

Rút ngắn các cuộc gọi chức năng lặp lại

Nếu bạn đã lặp lại các cuộc gọi đến một chức năng với một tên dài, chẳng hạn như thao tác canvas:

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

Cách truyền thống để rút ngắn nó sẽ là bí danh tên hàm:

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

Nếu bạn có đủ các cuộc gọi, một cách tốt hơn là tạo một chức năng thực hiện công việc cho bạn:

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

Nếu hầu hết các lệnh gọi hàm bị xiềng xích, bạn có thể làm cho hàm tự trả về, cho phép bạn cắt hai byte khỏi mỗi lệnh gọi liên tiếp:

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

Ví dụ sử dụng: 1 , 2


1
bạn có thể rút ngắn với toán tử liên kết :(l=::c.lineTo)(0,100)(100,100)(100,0)(0,0);c.stroke()
Downgoat

@Downgoat Cảm ơn, trình duyệt nào hỗ trợ điều đó? (Ngoài ra, từ những gì tôi thấy sẽ có lỗi trong cuộc gọi thứ hai, vì c.lineTonó không tự nhiên trở lại)
ETHproductions

bạn phải chà xát nó qua babel vì đây là một tính năng
ES7

3

Toán tử ràng buộc ::

Toán tử liên kết có thể được sử dụng để giúp rút ngắn byte qua các hàm lặp lại:

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

Ngoài ra, nếu bạn muốn sử dụng chức năng với một thisví dụ khác :

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

Tránh dấu phẩy khi lưu trữ nhiều dữ liệu

Nếu bạn có nhiều dữ liệu (ví dụ: chỉ mục, ký tự, số) mà bạn cần lưu trữ trong một mảng, bạn có thể nên để lại tất cả dấu phẩy. Điều này hoạt động tốt nhất nếu mọi phần dữ liệu có cùng độ dài chuỗi, 1 rõ ràng là tối ưu.

43 byte (đường cơ sở)

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

34 byte (không có dấu phẩy)

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

Nếu bạn sẵn sàng thay đổi quyền truy cập mảng của mình , bạn có thể giảm điều này hơn nữa, lưu trữ các giá trị tương tự như vậy:

27 byte (cùng dữ liệu, chỉ thay đổi truy cập mảng)

a=[..."376189452543276543"]

Tại sao chỉ có khối cuối cùng được tô sáng?
Máy

@CalculatorFeline Cảm ơn, đã sửa.
Chiru
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.