Swift: 840 lines
Nim: 768 lines
C#: 872 lines
Julia: 761 lines
Rust: 861 lines
Python: 550 lines
I am curious what a good APL-er [better than me] could do without trying to codegolf it, and how it performs.
41–50 of 52 posts
Swift: 840 lines
Nim: 768 lines
C#: 872 lines
Julia: 761 lines
Rust: 861 lines
Python: 550 lines
I am curious what a good APL-er [better than me] could do without trying to codegolf it, and how it performs.
Earlier quoted context omitted.
How well does this work? I've seen this library but never used it. Julia is really slow from a cold start, which limits its utility for scripting. About a year ago I was very off-put by someone on Reddit who sort of scolded me for wanting to use Julia as a general-purpose scripting language at the CLI. The Redditor said this wasn't as idiomatic usage of Julia. Idk how representative this is of the community at large.
By any chance, was your post about starting Julia in a tight inner loop? If so, this really isn't idiomatic and exactly one of the few cases where Julia isn't great. Though it can be circumvented.
I never did get around to trying the daemon mode thing someone suggested. Someday I'll have a reason to do this again and try it.
The author's experience is based on toy examples. Programming (and designing) in a professional setting is more than that, though. And the solutions are not the same for everyone: writing an Office plugin has different needs than writing a mobile game. For many of us, the availability of tooling and libraries is crucial. I'm not going to reinvent the web server by building on OS sockets or write a graphics rendering…
The same was said for Linux, Python and many other projects when they were at a very early stage. By your logic no community-driven project would ever be successful.
I feel the other way. LINQ is great for building pipelines. List comprehensions are kinda like English, but it gets ugly when you use the flattening feature, or when you want something more than just map and filter.
Looking at their C# code, they’re going at it in a fairly low-level way, with a lot of aliases for numeric types and a lot of casting between them. For example:
private static f32 distinct_arrangements_for(DieVal[] dieval_vec) {
var key_counts = dieval_vec.GroupBy(x=>x).Select(g=>(g.Key, (u8)g.Count()));
uint divisor=1;
uint non_zero_dievals=0;
foreach (var (key, count) in key_counts){
if (key != 0){
divisor *= factorial(count);
non_zero_dievals += count;
}
}
return factorial(non_zero_dievals) / divisor;
}
If you take out the unnecessary number type abuse and just use `int` everywhere, you get something much clearer: private static float DistinctArrangementsFor(DieVal[] dievals) {
var dievalCounts = dievals
.GroupBy(x => x)
.Where(group => group.Key != 0)
.Select(group => group.Count())
.ToArray();
var divisor = dievalCounts
.Select(factorial)
.Aggregate(1, (a, b) => a * b);
var nonZeroDievals = dievalCounts.Sum();
return factorial(nonZeroDievals) / divisor;
}
Also, even with all this hard work with weird types, this function has a suspicious return type, since it does integer division and returns a float.There are a few more weird things, like not using switch statements/switch expressions, or this function:
// calculate relevant counts for gamestate: required lookups and saves
public int counts() {
var ticks = 0;
var false_true = new bool[] {true, false};
var just_false = new bool[] {false};
foreach (var subset_len in Range(1,open_slots.Count)){
var combos = open_slots.Combinations(subset_len);
foreach (var slots_vec in combos ) {
var slots = new Slots(slots_vec.ToArray());
var joker_rules = slots.has(YAHTZEE); // yahtzees aren't wild whenever yahtzee slot is still available
var totals = Slots.useful_upper_totals(slots);
foreach (var _ in totals) {
foreach (var __ in joker_rules? false_true : just_false ){
// var slot_lookups = (subset_len * subset_len==1? 1 : 2) * 252; // * subset_len as u64;
// var dice_lookups = 848484; // // previoiusly verified by counting up by 1s in the actual loop. however chunking forward is faster
// lookups += (dice_lookups + slot_lookups); this tends to overflow so use "normalized" ticks below
ticks++; // this just counts the cost of one pass through the bar.tick call in the dice-choose section of build_cache() loop
} } } }
return (int)ticks;
}
There are a few weird things, like the pointless foreach loops at the end (which could be replaced with a bit of arithmetic) or the use of a foreach loop where a regular for loop would be better.If this code was rewritten in more idiomatic C#, it could be the basis of a good comparison between languages’ performance and syntax. But I’m not sure if it’s a good example at this point.
> most programmer convenience is sacrificed on the altar of "never crash". This makes it a good choice for sending a rocket to space When it comes to safety- or mission-critical software, crashing due to a dangling pointer, a stack overflow or a failed allocation are equally catastrophic, and not crashing but producing a wrong answer due to an algorithmic error is also equally catastrophic. In fact, producing the cor…
It depends on the domain. Sometimes a wrong answer is worse than a crash (e.g. a bank). Sometimes it is better (e.g. a game). Similarly the worst bugs can be crashes (random, non-deterministic, release-mode only, timing sensitive heisenbugs), or they can be undetected algorithmic errors. It entirely depends on the specifics. But either way, Rust's tradeoffs are easily worth it in terms of eliminating bugs unless you…
Earlier quoted context omitted.
By any chance, was your post about starting Julia in a tight inner loop? If so, this really isn't idiomatic and exactly one of the few cases where Julia isn't great. Though it can be circumvented.
Tooks some digging, but [here it is]( https://www.reddit.com/r/Julia/comments/n2mje4/comment/gwmt1... ). I was talking about invoking Julia from the shell as a script. Because loading packages takes time, I found that my script was significantly faster in Python than Julia. I never did get around to trying the daemon mode thing someone suggested. Someday I'll have a reason to do this again and try it.
In particular, functional languages (Haskell, Ocaml etc) and array-based languages (J, q/kdb or one of the many free clones out there).
Seeing what is capable in those paradigms is very eye opening.
I like nim but I've never really gotten used to the slicing syntax. I guess that I'm just too used to python, plus you have to be a bit careful to leave a whitespace when doing backwards indexing so that the operators work correctly. So python: a = [0,1,2,3,4,5] a[0:5] => [0,1,2,3,4] - half closed interval a[0:-1] => [0,1,2,3,4] - negative indexing a[:5] => [0,1,2,3,4] - skipping start index a[3:] => [3,4,5] - skippi…
> It's hard to feel happy grinding out a contrived LINQ expression for the same result as a "practically English" list comprehension in Python. I feel the other way. LINQ is great for building pipelines. List comprehensions are kinda like English, but it gets ugly when you use the flattening feature, or when you want something more than just map and filter. Looking at their C# code, they’re going at it in a fairly lo…
counts() was a throw-away for initializing the progress bar, so I was a little lazy there
Float you mention was needed for the looping calc at the call site.
Appreciate the feedback
Rust CUDA compilers do exist. https://github.com/Rust-GPU/Rust-CUDA