Is there a way of shortening fat-arrow functions?

15

From what I've seen throughout my time here on PPCG, most JavaScript entries involving fat arrow functions tend to be one of two camps:

  1. The simple ones that are capable of running as a single statement and returning an answer, straight off the bat, like x=(a,b)=>a*a+b

  2. The more complex ones that usually have curly braces because of the use of loops, and as a result require the use of a return statement.. like p=b=>{m=b;for(a=1;~-m;)--m,a*=m*m;return a%b}

Taking the above example from category 2 with the curly braces concept as proof-of-concept... Would there be a way to re-golf this code (or similar) like this so as to eliminate the curly braces as well as the return? I'm only asking this as this could potentially (not saying this will happen all the time) eliminate 8 bytes from a JS golfer's code. Are there any techniques that one could use in this instance? I've tried recursion, but the m=b statement has proven to be a bit of a bugbear, as I can't seem to shake it.

For the above code, how would one golf that further so as to eliminate the return statement, regardless of whether it golfs shorter or not?

WallyWest

Posted 2016-08-08T01:57:13.587

Reputation: 6 949

Answers

18

Use Recursion

I've found that recursion is (almost) always shorter than eval+for. The general way to convert from for to eval is:

for(a=n;b;c);d
(f=a=>b?f(c):d)(n)

So let's see your example:

b=>{m=b;for(a=1;~-m;)--m,a*=m*m;return a%b}

We can first simplify it to:

for(m=b,a=1;~-m;--m,a*=m*m)a%b;

What did we do here? Well we simply moved everything inside the for statement, this helps us reduce the amount of semicolons which is not directly better but almost always leads to some golf.


Let's put this in eval and compare it to the recursion version:

b=>{m=b;for(a=1;~-m;)--m,a*=m*m;return a%b}
b=>eval('for(m=b,a=1;~-m;--m,a*=m*m)a%b')
b=>(f=a=>~-m?(--m,f(a*=m*m)):a%b)(1,m=b)

The first part of the for loop (a=n), we can start that off by passing those variables in as arguments. The condition is simply: b?(c,f(a)):d where d is the return value. Usually c just modifies a so it can be merged into it. So we can golf it even more using what I've mentioned:

b=>(f=a=>~-m?(--m,f(a*=m*m)):a%b)(1,m=b)
b=>(f=a=>~-m?f(a*=--m*m):a%b)(1,m=b) // --m moved into a*=
b=>(f=a=>--m?f(a*=m*m):a%b)(1,m=b) // --m moved to condition

That said, as noted by @Niel is simplifying your algorithm. An algorithm golfy in one language may not be golfy in another so make sure to try different algoriths and compare them.

Downgoat

Posted 2016-08-08T01:57:13.587

Reputation: 27 116

1You've missed a big saving in simplifying the original code. ~-m is m-1, so the loop can be for(m=b,a=1;--m;a*=m*m)a%b; and the recursive version can be (untested) b=>(f=a=>--m?f(a*=m*m):a%b)(1,m=b) – Peter Taylor – 2016-08-08T06:23:21.430

1Sometimes you just have to use a different algorithm but in this case the best I could do was the same length as @PeterTaylor's answer: b=>b>1&(f=a=>--a<2||b%a&&f(a))(b) – Neil – 2016-08-08T09:24:11.750

11

Abuse eval.

It's simple. Instead of:

f=n=>{for(i=c=0;i<n;i++)c+=n;return c}

Use

f=n=>eval("for(i=c=0;i<n;i++)c+=n;c")

Eval returns the last evaluated statement. In this case, since the last evaluated statement would be c+=n, we would be left with c anyhow, saving two bytes.

f=n=>eval("for(i=c=0;i<n;i++)c+=n")

In general:

f=n=>eval("code;x")

is shorter than this, by a byte:

f=n=>{code;return x}

As a note, using graves to call eval to possibly save bytes doesn't work, since:

eval`string`

is equivalent to

["string"]

Helpful for obfuscation! Not so much for code golf.

Conor O'Brien

Posted 2016-08-08T01:57:13.587

Reputation: 36 228

2foo\string`` is always equivalent to foo(["string"]), it's just that many functions then cast the array back to the desired string. – Neil – 2016-08-08T09:25:39.343

@Neil Oh, how interesting! – Conor O'Brien – 2016-08-08T18:00:35.110