5 ms·
> 1.4 "Yo-Yo code" that converts a value into a different representation, then converts it back to where it started (eg: converting a decimal into a string and
by alexbecker 12y ago
> 1.4 "Yo-Yo code" that converts a value into a different representation, then converts it back to where it started (eg: converting a decimal into a string and then back into a decimal, or padding a string and then trimming it)
An important exception: many abstractions for data passing end up creating exactly this, but are good practice if you can afford them because it means one side of the yo-yo can be changed without breaking the other.
- wernercd 12y agoLikewise, 1.5 "Bulldozer" aka breaking up a function into subfunctions that aren't "useable everywhere". I can't claim I'm the best programmer, but I've used this to "simplify" programs and reduce nesting to great success. Add to that Aspect Oriented Programming in C# by using Attributes to add/remove logging to functions is easier if stuff is broking down into parts. Subroutines shouldn't simply be about reuse. I think that narrows their purpose too much. There is a place for code reuse. There is also a place for functions that only get called from one location.
- TeMPOraL 12y agoThinking about subroutines as code reuse is wrong. Subroutines are named blocks of code. When you're writing a program, you're building a language (or, I'd say, a series of languages) and subroutines are the basic building blocks for that. You want to split your function into a bunch of smaller ones if it makes the whole thing easy to read; reusability is just a (useful) side effect of a properly designed "language".
- pacala 12y agoGreat point. A subroutine is essentially assigning a signature (name, args, returns) to a block of code and never having to look into the details of the block of code again. A bad subroutine is a subroutine that hides nothing: all the internals details of the subroutine leak through the signature.
- yoklov 12y agoUnfortunately, a lot of bugs can be caused when a function that was only ever intended to be used from a single place gets called from another place, when it seems to do the needed work.
- TheLoneWolfling 12y agoHere's where nested function definitions can be useful.
- jpindar 12y agoThat's the one thing I miss from Pascal.
- seabee 12y agoSubroutines are not "named blocks of code" any more than they are facilitators of code reuse. Inline comments around a block of code also matches the concept of a named block of code. The point of subroutines is to gather the code that implements a particular concept, abstraction or algorithm in one place. Sometimes it coincides with code reuse or splitting your function into a bunch of smaller ones. The best way to tell if you are using subroutines effectively is if you can explain the effect of the subroutine without describing the implementation of it or (when splitting up a function) what it is trying to achieve for the function that calls it. If you can only describe it in relation to the function calling it, you fail. If you achieve code reuse and it's more than a line of code you get an automatic passing grade, it's just not necessarily a good grade.
- cheepin 12y ago> There is also a place for functions that only get called from one location Do it inline. If you need it to look separate from the other context try an immediately invoked function or at worst some comment demarcation. No need to spray functionality across a file(s) when you can do it all inline, especially if it is only called from one place. I'll admit I didn't come up with this 100% myself, but having tried both ways, I really like inlining. http://number-none.com/blow/john_carmack_on_inlined_code.html http://number-none.com/blow/john_carmack_on_inlined_code.htm...
- TeMPOraL 12y agoIf you're looking more at improving readability than performance, I like Lisp's approach to that, with flet and labels, that let you establish a bunch of functions limited to a scope (just like let does for variables) - you can split your function into few named subroutines and then define them inside that function. So you still get readability, and can keep the code that's only called once near the place it will be called from.
- wernercd 12y agoLooking at that, and considering A/B/C, I've seen myself using B most often - but I've not been afraid to use any of those. I'm not a fan of A, because I want to see the "bigger picture" first and then dive into details (minor 1/2/3/...) as needed. The reason for doing B vs C, in my experience with C#: * Logging. I can add, via PostSharp ( https://www.postsharp.net/features https://www.postsharp.net/features ) ( or similar extension/addons ) something as simple as `[LogDebug]` to a function and get logging goodness without having to mess with lines inside the code. PostSharp and Log4Net are my goto's. * Simplification - Partially included above (no need for a lot of logging code with the right tools). * Mental Gymnastics - its easier to see Step 1, 2, 3 when they are next to each other instead of pages apart. * Scope - only the parts that get sent in via parameters are worried about, and only a single return is normally needed. * Nesting - I've used minor functions to remove 3-4-5 levels of nesting before.
- yoklov 12y agoAside from logging (and games don't typically do a lot of logging), everything you mention is covered, and argued against, in the article.
- lostcolony 12y agoEspecially when you're aware of the difference between pure and impure functions, and when you believe in the importance of testing. I'd rather test a dozen small, mostly pure functions, and have the function that ties them all together be 'obvious' (and still integration tested), than to have one gigantic complex function that I need to try and test, and any related change causes a dozen tests to break.
- swatow 12y agoI would be suspicious of any subroutine whose function couldn't at least be described in words. So I agree that a function might be practically impossible to use in any other context, but it's worrisome to me when I see a function that doesn't do something that can be described in words. In most cases, after factoring out a function from a bigger function, it's usually a good opportunity to think about what this function really does, and to make it do something conceptually simpler (even if that is less tailored to its sole use case). This way, you can even write meaningful tests for the new function.
- mhomde 12y agoI agree, it's very seldom a method should be longer than 20 lines, and optimally around 6 lines. Any method over 50+ lines should require a long hard look if there isn't a way to simplify it. Inlining everything only gets messy. I would say that if you have a method composed of many methods you might want to encompass that into a separate class. That way you have everything needed to look at that method nicely in one place rather than interwoven with another class. Depends on the scope of the method though, you want neither a blob nor a poltergeist.
- rpcope1 12y agoCame here to mention, yo-yo code sometimes exists as a workaround for legacy implemenations: take XML-RPC for example, and the fact that the data types that can be passed are limited, and int is fixed at 32-bits. What happens when you need to pass a 64-bit int? You either need to convert it to its alphanumeric string representation or convert it raw into a character array, and then convert back on the server side. Often converting to the numeric string winds up producing more readable and maintainable code.