Search code examples
refactoringjtacit-programming

How to refactor this in J?


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.


Solution

  • 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.)