Solving for missing variable in Cocoa techniques?

Discussion in 'Mac Programming' started by GorillaPaws, May 7, 2009.

  1. GorillaPaws macrumors 6502a

    GorillaPaws

    Joined:
    Oct 26, 2003
    Location:
    Richmond, VA
    #1
    I was hoping you guys could give me a hand figuring out the best strategy for implementing a simple test app. Essentially, I want a window with 3 NSTextFields each corresponding to a, b, and c of the pythagorean theorem. The user would input values in say “a” and “c” and the program would calculate the value of the "b" variable. If this action were initiated by using a “Calculate” button on the GUI, I think my strategy would be to use if statements to determine which field had a value of nil and then use the appropriate version of the pythagorean theorem with the appropriate values substituted for the variables and calculate the result. Is this a reasonable way to go about doing this?

    Now if I wanted to get fancy and remove the calculate button and use a delegate method like textDidEndEditing: so the app would automatically calculate “b” when both “a” and “c” have been entered for example. The problem is that this method will be called after the first value is entered which means I would have to create a test at the beginning of the delegate method call to determine if there is a second field with a value or if the other two are nil. This doesn’t seem like a particularly elegant solution (especially if I ever wanted to use formulas with more than 3 variables), and I’m wondering if I’m on the wrong track here. Should I be doing this by creating an observing controller object and using a NSInvocation/messaging technique or something along those lines? Thanks in advance for pointing me in the right direction.

    I should also note that my ultimate intention is to create an app with many different formulas the user can select from, of which, many would have more than 3 variables.
     
  2. autorelease macrumors regular

    Joined:
    Oct 13, 2008
    Location:
    Achewood, CA
    #2
    a) Yes, you're on the right track. If you don't want to fully implement a computer algebra system in Objective-C (who wouldn't want to do that? :D) your best bet is to have three formulas (c=sqrt(a*a+b*b), a=sqrt(c*c-b*b), b=sqrt(c*c-a*a)) and use the right one depending on which field is blank.

    b) I don't see anything wrong with using textDidEndEditing:. If your text fields are elements in an array, you can easily loop over them to count the number of blank fields. If there is only one blank field, find out which one it is, validate the rest of the input fields, and perform the appropriate calculation. Alternatively, you could have the user enter "x" in the field to mark the variable he/she wants to solve for, or use a radio button.
     
  3. GorillaPaws thread starter macrumors 6502a

    GorillaPaws

    Joined:
    Oct 26, 2003
    Location:
    Richmond, VA
    #3
    Thanks for your help autorelease. I guess my main concern is that it seems like this method will be called way more often than it ever needs to and that makes me feel like I'm somehow approaching this the wrong way (kind of like the lady at the drive-through asking every customer if they want a filet-o-fish right off the bat, on the off chance that they say yes, instead of just asking for their order--if that analogy at all makes sense). I'm still a total noob so maybe this kind of thing is perfectly acceptable but it does seem a bit inefficient and backward (not that I'm worried about performance, more about learning how to solve problems the right way).

    Is there a way to do this with a controller object that keeps an eye on the 3 boxes, waits for 2 of them to have values and then sends the appropriate calculation method to the app controller (wrapped up as an NSInvocation) to be executed (or would something like this be horribly backwards and wrong?).
     
  4. jpyc7 macrumors 6502

    Joined:
    Mar 8, 2009
    Location:
    Denver, CO
    #4
    efficiency

    I don't know if there is such a controller object, but one should keep in mind that if there was one, it may very well be doing that loop through an array of objects. In other words, a library function may simplify the look of your user code, but is doing the same work you would have done if you had implemented it yourself.

    That said, one way to "increase" efficiency (but at a cost), is to simply increment (or decrement) a counter that says how many input fields have been edited. Then you don't need to loop through an array to find the nil field until the counter indicates only one field is left. Of course, the cost is that you must also prevent a user from re-editing a field once they've entered it. (I'm familiar with Java Swing GUI framework that can do this, but I don't know about Mac GUI.) This cost is probably unacceptable for a user application. As autorelease mentioned, you could also have an extra checkbox that indicates the field you want to solve for.

    Basically, in programming you can generally trade-off a lot of things. You could make the end user do more work (hit that checkbox) OR you could figure out the empty field in the computer. Generally, you should also consider overall efficiency. If your program will be used by lots of people, saving them time could be more important than saving programming time.
     
  5. lee1210 macrumors 68040

    lee1210

    Joined:
    Jan 10, 2005
    Location:
    Dallas, TX
    #5
    It is good to be developing a healthy interest in efficiency at this point, but doing something every time a user performs an action is not going to be the computationally intensive part of the app (unless the action is downloading a huge file, parsing it, uploading the result over a microwave link, etc.). Before i read jpyc7's post, my thought was about the same. In your controller, when you get the message that textDidEndEditing: from your NSTextField, I would just check to see if that NSTextField is blank or not. I'd keep a list of whether or not each field was filled in. When you get the notification, you check if the field was previous blank and now has a value, and increment a counter. If it was previously valued and is now blank, decrement the count. When the count = 2 you could do your calculation based on the value that's left.

    However, there are some problems with this approach. Now all 3 fields are filled in. So what will happen next? Say the user clears out one box. It will immediately be filled back in. Now the user can't clear fields to do a new calculation. Now the user is sad. Some ways around this would be to disable all of the fields when the calculation is performed, then have a "clear" button, etc. that clears all fields, sets the counter to 0, and re-enables all of the fields.

    Otherwise, you might do something like this:
    Have two boxes, with a dropdown under each.
    Have an = sign, and on the right side, have one box/field/label/whatever that is not editable.
    The dropdowns contain Hypotenuse and Leg. Once Hypotenuse is chosen in one, this option should be removed/disabled in the other. If leg is chosen, no problem. You'll have to keep track in case hypotenuse is changed to leg in one, etc.
    Once the user has filled in both text fields with numerical values, and chosen whether each is a leg or the hypotenuse, you can perform the calculation. If either is chosen as the hypotenuse, you can add the text "Remaining leg" under the result, and otherwise you can add "Hypotenuse".

    In this scheme, if the user changes any of the boxes, the calculation can just be refreshed. The cases are:
    leg changed to hypotenuse
    hypotenuse changed to leg
    length of either is changed
    length of either is removed

    In the first 3, you can just recalculate the result and if the type of result has changed. In the last case, you'd just clear the result.

    I know this adds some complexity, but it remains some of the burden from the user. It's perhaps not as "clever" as having the fields where they can enter data and you can "magically" know when to calculate, but users probably appreciate programmer cleverness less than usability.

    -Lee
     
  6. GorillaPaws thread starter macrumors 6502a

    GorillaPaws

    Joined:
    Oct 26, 2003
    Location:
    Richmond, VA
    #6
    Thanks guys, you both make some very smart/helpful observations. Lee, you were right that I was going for that "magic" effect, but I didn't really anticipate the inability to clear out fields issue that you point out. And honestly, is replacing a "calculate" button with a "clear all" button much of a UI advantage (it even seems worse in a way to me)?

    So it seems like going with the simple "Calculate" button would be the best approach. Lee, your solution incorporating the dropdowns is very clever, but I worry it would be difficult to expand upon as I add different formulas in the future, it also might be a bit more cumbersome for the user to navigate.

    If I wanted to eventually grow this concept to incorporate dozens of different formulas, how would that affect my architecture? I was thinking each formula would have it's own model class with ivars for each formula's particular variables and method calls like:
    solveForAWithB: C:
    solveForBWithA: C:
    solveForCWithA: B:
    or whatever would be appropriate for that particular class (e.g. a Momentum class might have a solveForMassWithMomementum: Velocity: method etc.).

    I'm still a bit confused how these classes would interact with my main app controller and then if I would use a NSViewController to manage adding in the appropriate UI elements specific to each Formula's requirements in the window. And how all of those relationships communicate (the AppController, the ViewController and the currently active model class). I realize that this kind of thing may be a bit over my head still, but I'd like to at least try to design my Pythagorean app with the thought that it will need to be somewhat modular, and will hopefully grow into something larger and more useful down the road. Thanks again for all of the help you guys have offered.
     
  7. autorelease macrumors regular

    Joined:
    Oct 13, 2008
    Location:
    Achewood, CA
    #7
    It will be easier if you abstract away the concept of named variables. Instead, you should refer to each of the variables with a numeric identifier. Then you could have a base class called Formula, and subclasses like PythagoreamTheorem, DistanceFormula, etc. that implement a method
    Code:
    solveForVariable:(int)varId withValues:(NSArray*)values
    You could then use a switch statement to determine which equation to use based on varId. The values array would contain the numbers the user entered in the text fields. I'll leave the rest up to you, but this solution can be easily adapted to formulas with any number of variables.

    Also, since an individual Formula has no state of its own (it just operates on its arguments), you could make solveForVariable:withValues: a class method. Then, you'd never actually have to create instances of Formula objects.
     
  8. GorillaPaws thread starter macrumors 6502a

    GorillaPaws

    Joined:
    Oct 26, 2003
    Location:
    Richmond, VA
    #8
    That's a very clever solution. I'm still a bit confused as to how I would organize the controllers. So let's say I create an NSTableView who's data source is an NSSet of objects that correspond to each subclass of our parent Formula class. When the user selects the DistanceFormula from the list the tableViewSelectionDidChange: method in the main AppController needs to tell the NSViewController to draw the appropriate UI elements that our DistanceFormula class needs: the correct number of NSTextFields, the appropriate labels and an image of the formula (I was thinking an image would be the easiest way to display a complicated formula such as the quadratic equation to the user but maybe there's a better way). Is that right?
     

Share This Page