How to refactor this in J?

2019-07-20 03:08发布

问题:

My newbie solution to Project Euler #1

+/((0=3|1+i.1000-1) +. (0=5|1+i.1000-1)) * (1+i.1000-1)

I know that this can be refactored, and transformed into a function, i don't know how to do it, and I would have to read all the labs to learn it.

回答1:

It isn't necessary to "handle zero" because adding zero won't change the answer so you can just use i. to generate your list of numbers below 1000, for example:

   i. 10
0 1 2 3 4 5 6 7 8 9

J works best with arrays so you should be able to ask for the residue (|) of 3 and 5 at the same time, you can use rank (") to control how the arguments are fed to residue:

   3 5 |"0 1 i. 10
0 1 2 0 1 2 0 1 2 0
0 1 2 3 4 0 1 2 3 4

The |"0 1 says to feed the left argument to | an-item-at-a-time while feeding the right arguments a-line-at-a-time. Because the right argument only consists of one line, it is fed repeatedly to each of the left argument items.

Now we can do the 0= to the whole array:

   0 = 3 5 |"0 1 i. 10
1 0 0 1 0 0 1 0 0 1
1 0 0 0 0 1 0 0 0 0

Insert an OR condition between the two items (lines) of the array:

  +./ 0 = 3 5 |"0 1 i. 10
1 0 0 1 0 1 1 0 0 1

Get the index of each 1 in the list/vector:

  I. +./ 0 = 3 5 |"0 1 i. 10
0 3 5 6 9

And sum:

 +/ I. +./ 0 = 3 5 |"0 1 i. 10

23

You can make this an explicit function/verb fairly easily:

   euler1=: verb define
+/ I. +./ 0 = 3 5 |"0 1 i. y
)

Or once you get the hang of tacit J you could define:

   euler1=: +/@I.@(+./)@(0 = 3 5 |"0 1 i.)


回答2:

  • Refactor 0= (will increases the program size)

+/((3|1+i.1000-1)+.&(0=])5|1+i.1000-1)*1+i.1000-1

  • Refactor 1+i.1000-1

+/(((3|])+.&(0=[)5|])1+i.1000-1)*1+i.1000-1

  • Refactor 1+i.1000-1 again

+/(*(3|])+.&(0=[)5|])1+i.1000-1

The only thing I couldn't refactor so far is the | operator