GOTO -vs- Structured form and state machines.
potatohead
Posts: 10,261
Rant mode = 1
Ok, this has been bugging me for a while now. I'm interested in the discussion, and perhaps in some tips to improve where I'm at coding wise.
Truth is, I like assembly code, and simple languages where I can just write stuff. This is, of course, both good and bad! Assembly language has a damn GOTO, and we all know it.
(and it rocks too)
I've been struggling with state machines and I honestly find putting just general logic into them very limiting. The thing is, I like to iterate through the logic, tuning it, expanding it, eventually reaching a state where it's rocking good, and I can build from there. With state machines, I find planning difficult because I don't always know where I'm headed, and because of that, I end up with a lousy state machine, spending more time manipulating the machine than I would just building out logic.
In the end, I like the value of structured program flow. I get that. What I don't get is why people can't have both. There are times when that value is good, and I think for building skill and coding general things that need to be done, there is a lot of utility in having that skill and the structure there. On the other hand, sometimes it's difficult to just deal with both at the same time.
There are times when I know I would be able to work something out, ugly, but it works. Then a refactor of it ends up sweet! What's the harm in this? That is one of the times when GOTO is excellent, and I like to learn that way because I get to see stuff along the way that I sometimes don't see when the journey is more structured.
Rant mode = 0
So then, given this dilemma, I find myself avoiding some things because it's just not productive. I need to change that. Any recommendations? When I'm working in PASM, it's often productive, if slow. But I don't mind the slow, what I do mind is having to focus on more than is necessary to realize HOW to do something. Like it or not, sometimes that comes well before doing it WELL, and I feel constrained in SPIN at times, because of the DO IT WELL piece always there.
This is not an "Add GOTO to SPIN" post. That's another discussion. Really, this is a warm fuzzy for to convince me to go do some boring work, and some references that might help in that, more than anything else.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Propeller Wiki: Share the coolness!
8x8 color 80 Column NTSC Text Object
Safety Tip: Life is as good as YOU think it is!
Ok, this has been bugging me for a while now. I'm interested in the discussion, and perhaps in some tips to improve where I'm at coding wise.
Truth is, I like assembly code, and simple languages where I can just write stuff. This is, of course, both good and bad! Assembly language has a damn GOTO, and we all know it.
(and it rocks too)
I've been struggling with state machines and I honestly find putting just general logic into them very limiting. The thing is, I like to iterate through the logic, tuning it, expanding it, eventually reaching a state where it's rocking good, and I can build from there. With state machines, I find planning difficult because I don't always know where I'm headed, and because of that, I end up with a lousy state machine, spending more time manipulating the machine than I would just building out logic.
In the end, I like the value of structured program flow. I get that. What I don't get is why people can't have both. There are times when that value is good, and I think for building skill and coding general things that need to be done, there is a lot of utility in having that skill and the structure there. On the other hand, sometimes it's difficult to just deal with both at the same time.
There are times when I know I would be able to work something out, ugly, but it works. Then a refactor of it ends up sweet! What's the harm in this? That is one of the times when GOTO is excellent, and I like to learn that way because I get to see stuff along the way that I sometimes don't see when the journey is more structured.
Rant mode = 0
So then, given this dilemma, I find myself avoiding some things because it's just not productive. I need to change that. Any recommendations? When I'm working in PASM, it's often productive, if slow. But I don't mind the slow, what I do mind is having to focus on more than is necessary to realize HOW to do something. Like it or not, sometimes that comes well before doing it WELL, and I feel constrained in SPIN at times, because of the DO IT WELL piece always there.
This is not an "Add GOTO to SPIN" post. That's another discussion. Really, this is a warm fuzzy for to convince me to go do some boring work, and some references that might help in that, more than anything else.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Propeller Wiki: Share the coolness!
8x8 color 80 Column NTSC Text Object
Safety Tip: Life is as good as YOU think it is!
Comments
Terms like "Structured" can be dangerous. You need to be careful to separate the "intent and meaning" from what tends to be laid out as "rules". Even if the intent is defined by "rules", just because there is a set of "rules", does not mean that these rules are perfect, and should not evovle and/or be adapted.
To paraphrase "Pirates of the Carabean", like the Pirate Code, they're not really "rules", more along the line of "suggestions".
GOTO, of and by itself, is not "evil". What people often do with it is another story.
Structured code should not be though of as "don't GOTO", but more as "provide a straight, logical and "structured" flow through the software. There are other aspects (e.g. side effects, "black box", etc.) but those are not the point here.
If you use GOTO to jump back and forth and all around, that would not be "structured" and leads to/is "spagetti code".
If you come into a stream of code, and use GOTO to "exit" the code cleanly, or go to a "sub routine" and come back, that's still "structure", you're just using the tools at hand. If the language supports other methods (sub routines, etc.), the use of GOTO becomes less necessary/desireable.
When you get down to microcontrollers, the other factor is efficiency. Both in terms of memory usage and execution speed.
There were things I used to do on a PC when programming in C/C++ that I don't do now. I did them at the time because they were fast, and used minimal resources. I don't even remember what some of them are now. The problem was that when some programmers looked at the code, they went "huh?" because it took some time to work through what was being done. With current PC speed and resources, I can write clear, easy to maintain code (for me, and those that follow), and it still executes mroe than fast enough. For that matter, I work less in C and more in C#, it's still fast enough.
Point being, you need to adapt to your environment.
Is your code:
* Reliable and functional?
* Clear to anyone (with an appropriate background) reading it?
* Can the flow be easily followed?
* Can the code be easily maintained, not just today, but next week, next year, etc., by you or others?
If the above is true, than regardless of if it's "structured" or not, you probably have good code.
On the other hand, if you set the code aside and return next week, and it takes a couple hours to figure out where to fix a given "problem", or add a new feature, then you probably have been taunting the Happy Fun Ball that is the "good code guide". (And we all know what happens when you taunt the Happy Fun Ball.)
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
John R.
Click here to see my Nomad Build Log
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Propeller Wiki: Share the coolness!
8x8 color 80 Column NTSC Text Object
Safety Tip: Life is as good as YOU think it is!
Your ·Rant mode and questions and your·point of view· are good thing··to ask because I have ask the same thing my self just have not posted them on the fourm
I have the same problem my self ........>>>>>>>>>>>
So then, given this dilemma, I find myself avoiding some things because it's just not productive. I need to change that. Any recommendations? When I'm working in PASM, it's often productive, if slow. But I don't mind the slow, what I do mind is having to focus on more than is necessary to realize HOW to do something. Like it or not, sometimes that comes well before doing it WELL, and I feel constrained in SPIN at times, because of the DO IT WELL piece always there.
This is not an "Add GOTO to SPIN" post. That's another discussion. Really, this is a warm fuzzy for to convince me to go do some boring work, and some references that might help in that, more than anything else..........<<<<<<<<<<<<<<
·
My goal is to write good Structured· code like John R point out here >>>>
When you get down to micro-controllers, the other factor is efficiency. Both in terms of memory usage and execution speed.
It take time to learn the right way·to write code <<<<<<<<<<<<<
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
·Now wanting to learn Spin· Thanks for any··that you may have and all of your time finding them
·
·
·
·
Sam
Post Edited (sam_sam_sam) : 4/23/2010 11:46:05 PM GMT
I started doing industrial programming 6 years ago.
I couldn't survive without state machines.
They bundle up together related code into more manageable blocks.
The fact that it needs to be a state machine, is that there is typically a "scan loop".
The program needs to continually execute from start to end.
the whole "scan inputs, process, produce outputs" loop.
There is often too much code to ever hope to just run through it all, testing for every condition.
The code also cannot "stop" in a "wait for input to change" tight WHILE loop.
if you do that, then everything else grinds to a halt. (plus you get a watchdog timeout).
So you use "states", which are typically manifested as the CASE statements (or a huge block of IF-THEN-ELSIF-ELSIF-ELSE-ENDIF).
As for implementation, it doesn't work (now and in the near future) where there is so much parallel processing going on, to have a bunch of simultaneous "microthreads" or whatever.
It would waste so much time on the overhead of just context switching from thread to thread to do maybe just a silly Output = A AND B AND NOT C OR D snippet.
So, instead, it is do what you need to do for block A depending on the state it's in.
So what you need for block B.
Chunk C might always need to execute.
Block D only needs to execute when Block B is in a certain state.
Etc.
As for using a structured approach, in my area it just makes sense again. There are, physically, multiple sections to the production machine.
It would make sense to separate the code and variables for the one section from the other.
It doesn't make sense to have 10 lines of code for the one section, suddenly 1 line for the other section, 15 lines for the first, and 2 for the second. I'd go crazy as more and more physical sections are added or substituted.
This is all that structured programming means to me.
As for sharing information between sections: One section can depend on the variables of the other (nesting, parent-child, whatever you want to call it). Or you can have a higher-level third section that is the intermediary between the two. This is all that "inheritance" means to me.
I've never had to use a goto other than to just jump over a section of unneeded code.
Even then it's conditional.
(if it's skipping a debug statement, the condition to execute or not is at a higher level than even the code itself)
There's an unproductive code pattern that goes like this:
A=0
IF A=0 THEN GOTO THERE:
A = A + 1
B = 2
IF A=1 THEN
THERE:
RETURN A+B
END IF
"A" obviously isn't "1" even though we tested for it just above. Also, who knows what we return for the A + B calculation?
Even though the assertion in the IF statement above seems to be true, the gotcha is in the jumping randomly in somewhere.
Things like this is where GOTO is considered harmful, when the distance between jumps is huge, or there is more than one of them and they're tangled together.
At the top of your program, in a comment, MAKE A LIST OF STATES. Generate values for them via enum. This is the master index for your project.
Next, for each state, write a SET routine that puts your entire project in that state. Assume nothing and omit nothing; these routines should put the project in that state no matter where it was before. This will seem wasteful in some cases but trust me, it's necessary and it works.
Finally, for each event, write a handler with a big CASE statement that determines if the event happens during state X, you will set state Y. Some processing may be involved in the transition, which will probably be the purpose of your project.
In projects small enough for it to be practical, it can help to draw a "state map" where the "islands" are the states and the routes between them are events.
Remember, neither SET events nor event handlers should ever hang in loops waiting for something to happen. If you are doing something like that, it's a new state that should be added to the state list and set and handled.
If you approach your state machine projects with this paradigm in mind, I think it will all quickly make sense for you.
FWIW: I have been programming, mostly in assembler, for longer than most of you have been alive - LOL and :-(
The big thing is to make sure you can read the code. GOTO's are an important and required part of assembler. If your code becomes obfuse because of other constraints (timing & space are the two main ones) then just make particularly sure it can be read later. Otherwise you (and others) may have to decode what you are doing much later. That is why I posted the code to you with the longer code as a comment block so you and others could easily decipher what was really happening.
Just use common sense - and often missed point and recently deceased as I understand from an email I received recently. LOL
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Links to other interesting threads:
· Home of the MultiBladeProps: TriBlade,·RamBlade,·SixBlade, website
· Single Board Computer:·3 Propeller ICs·and a·TriBladeProp board (ZiCog Z80 Emulator)
· Prop Tools under Development or Completed (Index)
· Emulators: CPUs Z80 etc; Micros Altair etc;· Terminals·VT100 etc; (Index) ZiCog (Z80) , MoCog (6809)·
· Prop OS: SphinxOS·, PropDos , PropCmd··· Search the Propeller forums·(uses advanced Google search)
My cruising website is: ·www.bluemagic.biz·· MultiBlade Props: www.cluso.bluemagic.biz
localroger, could I pester you for a skeleton and sample state diagram? Could be any common thing. That approach appeals to me, because I like to incrementally build things up. Part of that is time. I get little chunks here and there. The other part of it is having not worked on big projects. The biggest thing I wrote was an OpenGL CAD file viewer, and it wasn't all that big.
(by big, I mean having some depth, not massive lines of code)
Whicker, I'm thinking on yours right now, mulling over the strategy post localroger put here.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Propeller Wiki: Share the coolness!
8x8 color 80 Column NTSC Text Object
Safety Tip: Life is as good as YOU think it is!
Next, for each state, we write a driver that puts the whole system in that state, no matter what it was doing before:
You get the idea. Note that each routine completely sets up everything, not just the IO concerned with that state. That way, whether you're entering fill from idle or pause it works the same way. Now we use these to implement state transitions based on whatever's happening:
And this is pretty much the entire application. I've left a couple of things out as an exercise; you really need a time delay between ending the dump and starting the fill (really you need to wait for stability and re-zero the scale, that's another state, and if you wrote the app this way adding it is pretty trivial). You also need a way to weigh the last batch that cascades through to dumping it without starting a new fill.
I've stumbled on that repeatedly, more in a bit.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Propeller Wiki: Share the coolness!
8x8 color 80 Column NTSC Text Object
Safety Tip: Life is as good as YOU think it is!
Please explain something in more detail
In this routine you have it going from routine label to the next· routine label··NO where you do·you seem to have a ELSE or an ELSEIF
I have try to write code like this for a Basic Stamp and most of the time if I do not write an ELSE or an ELSEIF· I can not seem to get it to work··the right way
Or· am I not understanding· just how to do this the right way
set_idle
··repeat
····case·mstate
·······st_idle:
··········if·user_presed_key
············set_fill
·······st_fill:
··········if·user_pressed_key
············set_pause
··········elseif·scale_weight·>·target
············set_weigh
·······st_pause:
··········if·user_pressed_key
············set_fill
··········elseif·user_held_key_down
············set_finish
·······st_weigh:
··········if·scale_stable
············total·+=·scale_weight
············set_dump
·······st_dump:
··········if·scale_weight·<·zero_interlock
············set_fill
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
·Now wanting to learn Spin· Thanks for any··that you may have and all of your time finding them
·
·
·
·
Sam
Post Edited (sam_sam_sam) : 4/25/2010 6:52:10 PM GMT
In the example you quote, the case statement is repeated over and over because it's the only thing at the next level of indentation under the repeat. If mstate == st_idle, then the case statement would execute whatever is block-indented under the st_idle: case line. That in turn is the if statement, and if it's true (presumably a routine that checks and returns true or false for this) the if statement executes whatever is block-indented beneath it, which is the call to set_fill. Since the line beneath set_fill is outdented past the if statement's position that ends the if statement, tosses back to the case statement which figures out it's found and executed a case so falls through. And since there's nothing else after it execution falls back to the repeat, which runs the case statement again.
SPIN is really readable because of that. sam_sam_sam (I am), consider turning that feature on, then write a few statements, and it will be very clear what's going on.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Propeller Wiki: Share the coolness!
8x8 color 80 Column NTSC Text Object
Safety Tip: Life is as good as YOU think it is!
Thank You for your explanation of the example that you gave that help allot although I did have to reread your post a few time to completely
understand the example
I also want to learn the SPIN as well·
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
·Now wanting to learn Spin· Thanks for any··that you may have and all of your time finding them
·
·
·
·
Sam