> I can read the C++ (not C), though, and improve it,
Can you give an example of some improvements you see that are directly obvious from the C code?
I'm especially interested in a process that reveals the sqrt optimisation.
> whereas the two lines above are impenetrable.
It takes practice to learn a new language. For now, you will have to take my word that the former of the lines is almost a literal translation of the problem, and to an experienced q developer, directly suggests the second line. I hoped the illustration would suggest this, but communicating alien things is hard.
Languages that have that property are extremely valuable to me: There are a lot of languages that have a nice notation leading from problem to code (lisp, haskell, etc; at least some of the time) but it is much more rare that we have a notation that directly suggest further (better) notations, and myself having programmed in C, C++, Perl, Python, Java, Forth, Fortran, Cobol, Lisp, and so on for decades, I'm telling you that this seems to happen more with array languages than with any other language or language-family that I've had experience with.
If that intrigues you, I can try and give you a crash course:
- til x is the vector of integers from 0 to x. In C it's a little like: r=calloc(sizeof(int),x);for(i=0;i<x;++i)r[i]=x;
- x mod y is modulo. Similar to x%y in C, but it generalises to vectors.
- x mod/: y shows each-right (/:) which composes to the operator on the left. This builds a 2d array of each x argument (columns) mod each y argument (rows).
- til[x] mod/: til[x] is thus the 2d array of integers from 0 to x mod each right the same list.
- x=y is equals. Similar to x==y in C, but it generalises to vectors.
- 0=til[x] mod/: til[x] is thus a bitmap of locations where (columns:0..x) mod (rows:0..x) = 0
- sum adds up the values in a vector. applied to a 2d array (including a bitmap) returns the sum of each column.
- functions are written with curly braces. In a function, x is the first argument, y is the second.
- {x*x} is thus a function that squares its result.
- mod[;2] is a projection. It is actually the function {x mod 2}
- where returns the locations of 1's in a list.
- where mod[;2] ... is thus the locations of odd values.
- sqrt x returns the square-root of x, expressed as a float. this too generalises to vectors, but we do not take advantage of this.
- ceiling x is the same as ceil(x) except it too generalises to vectors (we aren't using this).
With that in hand, you should be able to carefully decode each line: They're read from left to right, and q composes easily.
> I can also lift the core of the C++ code into a function and reuse it, which doesn't appear possible in the other language.
That's generally true for Python or Java, but q programmers have an excellent C binding. It's as simple as:
k(0, "{where mod[;2] sum 0=til[x] mod/: til x}", kj(n), 0)
- k(f,s,x,[y,[z,...]],NULL) calls into k. f must be zero (in-process) in this case. s is the script. x, y, z, (etc) are arguments terminated by null.
- kj(n) returns a variant (type tagged) from a long long.