7 ms·
o_node := graph.GetNodeByName(name) var ret []string for _, node := range o_node.connectedNodes() { if !node.isHidden { ret = append(ret, node.nam
by desumeku 2y ago
o_node := graph.GetNodeByName(name)
var ret []string
for _, node := range o_node.connectedNodes() {
if !node.isHidden {
ret = append(ret, node.name)
}
}
return ret
- stouset 2y agoThere is just no way that reasonable people consider this to be clearer. One certainly might be more familiar with this approach, but it is less clear by a long shot. You've added a temp variable for the result, manual appending to that temp variable (which introduces a performance regression from having to periodically grow the array), loop variables, unused variables, multiple layers of nesting, and conditional logic. And the logic itself is no longer conceptually linear.
- desumeku 2y agoEverything you said is true for both of our programs, the only difference is whether or not it's hidden behind function calls you can't see and don't have access to. You don't really think that functional languages aren't appending things, using temp vars, and using conditional logic behind the scenes, do you? What do you think ".filter(node => !node.isHidden)" does? It's nothing but a for loop and a conditional by another name and wrapped in an awkward, unwieldy package. >which introduces a performance regression from having to periodically grow the array This is simply ridiculous, do you just believe that the magic of Lisp/FP allows it to pluck the target variables out of the sky in perfectly-sized packages with zero allocation or overhead?
- JadeNB 2y ago> You don't really think that functional languages aren't appending things, using temp vars, and using conditional logic behind the scenes, do you? What do you think ".filter(node => !node.isHidden)" does? It's nothing but a for loop and a conditional by another name and wrapped in an awkward, unwieldy package. But the whole point of higher-level languages is that you don't have to think about what's going on behind the scenes, and can focus on expressing intent while worrying less about implementation. Just because a HLL is eventually compiled into assembler, and so the assembler expresses everything the HLL did, doesn't mean the HLL and assembler are equally readable. (And I think that your parent's point is that "awkward, unwieldy package" is a judgment call, rather than an objective evaluation, based, probably, on familiarity and experience—it certainly doesn't look awkward or unwiely to me, though I disagree with some of the other aesthetic judgments made by your parent.)
- stouset 2y ago> the only difference is whether or not it's hidden behind function calls you can't see and don't have access to. You "can't see and don't have access to" `if`, `range`, or `append` but somehow you don't find this a problem at all. I wonder why not? > You don't really think that functional languages aren't appending things, using temp vars, and using conditional logic behind the scenes, do you? By this metric all languages that compile down to machine instructions are equivalent. After all, it winds up in the same registers and a bunch of CMP, MOV, JMP, and so on. `.distinct()` could sort the result and look for consecutive entries, it could build up a set internally, it could use a hashmap, or any one of a million other approaches. It can even probe the size of the array to pick the performance-optimal approach. I don't have to care. > [".filter(node => !node.isHidden)" is] nothing but a for loop and a conditional by another name and wrapped in an awkward, unwieldy package. This is honestly an absurd take. I truly have no other words for it. map, filter, and friends are quite literally some of the clearest and most ergonomic abstractions ever devised.
- desumeku 2y ago> it could build up a set internally, it could use a hashmap, or any one of a million other approaches. It can even probe the size of the array to pick the performance-optimal approach. I don't have to care. Well, this is probably why functional programming doesn't see a lot of real use in production environments. Usually, you actually do have to care. Talk about noticing a performance regression because I was simply appending to an array. You have no idea what performance regressions are happening in ANY line of FP code, and on top of that, most FP languages are dead-set on "immutability" which simply means creating copies of objects wherever you possibly can... (instead of thinking about when it makes sense and how to be performant about it)
- torben-friis 2y ago>Well, this is probably why functional programming doesn't see a lot of real use in production environments The usual map/filter/reduce is everywhere in production. Python, java, js, ruby, c#... You could even argue that lack of generics hurt Go's popularity for a while precisely for that usecase.
- AdieuToLogic 2y ago> Everything you said is true for both of our programs, the only difference is whether or not it's hidden behind function calls you can't see and don't have access to. This is a key difference between imperative programming and other paradigms. > You don't really think that functional languages aren't appending things, using temp vars, and using conditional logic behind the scenes, do you? A key concept in a FP approach is Referential Transparency[0]. Here, this concept is relevant in that however FP constructs do what they do "under the hood" is immaterial to collaborators. All that matters is if, for some function/method `f(x)`, it is given the same value for `x`, `f(x)` will produce the same result without observable side effects. > What do you think ".filter(node => !node.isHidden)" does? Depending on the language, apply a predicate to a value in a container, which could be a "traditional" collection (List, Set, etc.), an optional type (cardinality of 0 or 1), a future, an I/O operation, a ... > It's nothing but a for loop and a conditional by another name and wrapped in an awkward, unwieldy package. There is something to be said for the value of using appropriate abstractions. If not, then we would still be writing COBOL. 0 - https://en.wikipedia.org/wiki/Referential_transparency https://en.wikipedia.org/wiki/Referential_transparency
- porridgeraisin 2y ago> performance regression What? Golang append()s also periodically grow the slice. > Conditional logic it's just a single if, really, the same thing is there in your filter() > Multiple layers of nesting 2... You're talking it up like it's a pyramid of hell. For what it's worth, I've seen way way more nesting in usual FP-style code, especially with formatting tools doing func( args ) For longer elements of the function chain.
- stouset 2y ago> What? Golang append()s also periodically grow the slice. If you already know the size of the result (there are no filtering operations), the functional approach can trivially allocate the resulting array to already have the correct capacity. This happens with zero user intervention. IIRC the Rust optimizer basically emits more or less optimal machine code (including SIMD) for most forms of iteration.
- porridgeraisin 2y agoWe are talking about making an array with unique elements here. You cannot know the correct capacity for that without overallocating. If overallocating is indeed OK for your usecase, then you can do so yourself uniq := make([]MyObject, 0, len(my_objects))
- stouset 2y agoI was talking more in the general case. Yes, you can always just write more and more and more code to fix these things. Or you could just… not write more code and still get optimal performance.
- porridgeraisin 2y ago> just... Not write more code But the abstractions don't really adapt themselves to be performant in each usecase the way you imagine. I gave an example of c# distinct() above. Sure, they can. But do they? No. They only save anything at all for trivial usecases, where the imperative code is also obvious to parse.
- DonHopkins 2y agoo_god!