Recursive method Objective-C - ios

I'm checking is first letter of string is 0, if it is remove it and call again method to check is there is still 0. I've debugged this and it seems like when it accomplish number without 0, it goes backwards. Code:
-(NSString *)deleteZerosOnFirst:(NSString *)card
{
NSString *firstLetter = [card substringToIndex:1];
if ([firstLetter isEqualToString:#"0"]) {
card = [card substringFromIndex:1];
[self deleteZerosOnFirst:card];
NSLog(#"CARD: %#", card);
return card;
}
else {
NSLog(#"CARD: %#", card);
return card;
}
}

The main problem is that you're not using the result of the recursion. The line of code where you call yourself should say this:
card = [self deleteZerosOnFirst:card];
Also, you're calling deleteZerosOnFirst before you do the NSLog. Reverse the order of these two lines. That will at least give you your debug output in the right sequence.

Here's your recursive call:
[self deleteZerosOnFirst:card];
That doesn't modify the string that card references. It creates and returns a new string. You're ignoring the returned string. You want this:
card = [self deleteZerosOnFirst:card];
But this is really a lot simpler:
#implementation NSString (withoutLeadingZeroes)
- (NSString *)withoutLeadingZeroes {
NSString *s = self;
while ([s hasPrefix:#"0"]) {
s = [s substringFromIndex:1];
}
return s;
}
#end

Related

Parsing id to NSString

When parsing API responses, sometimes I can not rely on strings being embedded in quotation marks. ID's are a good example of this, where some API's will send the numerical ID as a string while some will send it as a number.
What is a good practice when parsing such a value? If I simply parse it to an NSString like so:
NSString *myID = (NSString *)message["myID"];
I can end up with an NSString object that somehow contains (long)123.
And using stringValue would cause issues when the value is actually already sent as a string (since NSString does not have a stringValue function).
A way that works, but is somewhat ugly, is this:
id myID = (NSString *)message["myID"];
if ([myID respondsToSelector:#selector(stringValue)])
{
myID = [myID stringValue];
}
You could do something like:
id myID = message["myID"];
if ([myID isKindOfClass:[NSString class]]) { ... }
else { ... }
As long as this logic is encapsulated inside data parser and is opaque for your api users (i.e. they will always get a string) any approach is fine, e.g.:
- (NSString*)parseID:(NSDictionary*)message {
id rawID = message["myID"];
if ([rawID isKindOfClass:[NSString class]]){
return rawID;
} else if ([rawID isKindOfClass:[NSNumber class]]) {
return [(NSNumber*)rawID stringValue];
} else {
// We might still want to handle this case.
NSAssert(false, #"Unexpected id type");
return nil;
}
}
Alternative is to define stringValue in extension, so any possible objet will respond to selector:
#implementation NSString(JSONStringParsing)
- (NSString *)stringValue {
return [self copy];
}
#end
Why not just use description?
NSArray *objects = #[
#NSIntegerMin,
#NSIntegerMax,
#"123456789"
];
for (id object in objects) {
NSString *stringObject = [object description];
NSLog(#"%# -> %# | %#", [object className], [stringObject className], stringObject);
}

iOS what does performSelector:withObject:withObject: really do?

What's different between selectors with same arguments type but different arguments sort?
I got these two selectors with same arguments type.
- (void)methodWithCallBack:(void(^)(void)) cb double:(double)value {
NSLog(#"%s %f", __PRETTY_FUNCTION__, value);
if (cb) {
cb();
}
}
- (void)methodWithDouble:(double)value callBack:(void(^)(void)) cb {
NSLog(#"%s %f", __PRETTY_FUNCTION__, value);
if (cb) {
cb();
}
}
But when performSelector:withObject:withObject: called with these selectors, I got different result.
[self performSelector:#selector(methodWithDouble:callBack:) withObject:#(2.5) withObject:[^(void){
NSLog(#"Test Call Back Double");
} copy]];
[self performSelector:#selector(methodWithCallBack:double:) withObject:[^(void){
NSLog(#"Test Call Back Double");
} copy] withObject:#(2.5)];
How does this happend? What does performSelector:withObject:withObject: really do?
I have no idea what #(2.5) is. NSNumber literal is #2.5. a double is non object and NSNumber is object. You should pass an NSNumber.
On a second note: I feel weird that Apple created performSelector:withObject:withObject:. A single "withObject" is suffice actually, you just pass an NSArray with objects you want to pass to that.
Eg.
NSNumber *var1 = #2.5;
NSString *yourMom = #"Sally";
NSArray *params = [NSArray arrayWithObjects:var,yourMom,nil];
[self performSelector:#selector(goMethod:) withObject:params];

Objective C delete last NSString in NSMutableArray

I am trying to get the last NSString from a NSMutableArray, and delete it if it's empty.
Here is the code I am using:
- (void)viewWillAppear:(BOOL)animated {
if ([notes.data length]==0){
[storedText removeLastObject];
}
[self.tableView reloadData];
}
data is the NSString and storedText is the NSMutableArray. This code deletes the NSString even if it's not empty. I want it to keep the string if it contains text.
You should try something like this:
- (void)viewWillAppear:(BOOL)animated {
if ([[[storedText lastObject] data] length]==0){
[storedText removeLastObject];
}
[self.tableView reloadData];
}
BTW, it seems that your hypothesis that notes.data is the same string as the last object in storedText is not correct. This leads me to suspect that you have other kinds of errors in your code that you should also investigate. In other words, what you can expect is that your notes.data is not correct -- indeed, you should at least "update" it after you remove the last object from the array:
- (void)viewWillAppear:(BOOL)animated {
if ([[[storedText lastObject] data] length]==0){
[storedText removeLastObject];
notes = [storedText lastObject]; //-- this will be the *new* last object after removal
}
[self.tableView reloadData];
}
but I have no clue what you do with notes.data, so I do not know if this by itself is enough to bring it back to being consistent with your hypothesis.
Ideally before you add any item to storedText you would check that it wasn't nil or an empty string, then you wouldn't have the problem.
Otherwise, get the last item from storedText and check it (using isEqualToString: or a length check) and then remove it if it matches.
Follow what Wain said. Do this.
//total array size
int count = [storedText count];
int lastItemIndexInt = count - 1;
//get last object in array which is a string
NSString *str1 = [storedText objectAtIndex:lastItemIndexInt];
if ([str1 isEqualToString:#""] || [str1 length] < 1)
{
//string is null / blank, remove it
[storedText removeObjectAtIndex:lastItemIndexInt];
}

How to randomize letters correctly from an NSString

I am creating a word scrambler and I am having issues randomizing the letters. When the letters get randomized, it doesn't make sense.
For example, the word PARK shows as AAPA. So, as you can tell it won't make sense for the user when it is time to unscramble.
Just so you know, I am using a .plist file to hold the words.
This is the code I am using to randomize the letters:
_words = [NSMutableArray arrayWithCapacity:scramblelength];
for (int i=0;i<scramblelength;i++) {
NSString *letter = [scramble substringWithRange:[scramble rangeOfComposedCharacterSequenceAtIndex:arc4random()%[scramble length]]];
Then, I am creating UIImageViews to display the scrambled words:
if (![letter isEqualToString:#""]) {
GameView *boxes = [[GameView alloc] initWithLetter:letter andSideLength:boxSide];
boxes.center = CGPointMake(xOffset + i*(boxSide + kTileMargin), kScreenHeight/4*3);
[self.scrambleView addSubview:boxes];
[_words addObject:boxes];
What am I doing wrong here? I would like for the letters in the scrambled words to make sense.
Please help, I am stuck on this one!
Thanks!
As long as your string length will fit in 32 bits, this should be fine. If not, I would replace arc4random_uniform with a uniform random number generator in C++ and compile this as an Objective-C++ module.
The code simply iterates through the string, and swaps each composed character sequence with some random composed character sequence from the same string.
Sorry, that's what happens when you are arrogant and just type out code. Let me know if you have trouble with this one...
For much larger strings, there is a more efficient way, but this seems to do the trick.
NSMutableString category...
#interface NSMutableString (Scramble)
- (void)scramble;
#end
#implementation NSMutableString (Scramble)
static void
swapRanges(NSMutableString *string, NSRange iRange, NSRange jRange)
{
// Need to replace the "trailing" component first
if (NSEqualRanges(iRange, jRange)) return;
if (iRange.location > jRange.location) {
NSRange tmpRange = iRange;
iRange = jRange;
jRange = tmpRange;
}
NSString *iString = [self substringWithRange:iRange];
NSString *jString = [self substringWithRange:jRange];
[string replaceCharactersInRange:jRange withString:iString];
[string replaceCharactersInRange:iRange withString:jString];
}
- (void)scramble
{
for (NSUInteger i = 0; i < self.length; ++i) {
NSRange iRange = [self rangeOfComposedCharacterSequenceAtIndex:i];
NSUInteger j = arc4random_uniform(self.length);
NSRange jRange = [self rangeOfComposedCharacterSequenceAtIndex:j];
swapRanges(self, iRange, jRange);
}
}
#end
NSString category...
#interface NSString (Scramble)
- (NSString*)scrambledString;
#end
#implementation NSString (Scramble)
- (NSString *)scrambledString
{
NSMutableString *result = [self mutableCopy];
[result scramble];
return [result copy];
}
#end
Sample use...
[someMutableString scramble];
NSString *mixedUp = [someString scrambledString];
Or, if you are comfortable with C++, convert to a std::wstring, call std::random_shuffle, then convert that to a NSString. Lots less bugs when using proven, well tested code.
When you are getting a random letter, you need to do something to remove that letter from your NSMutableArray (ie the word's letters when in order). So as you iterate through the word, each time there are fewer characters remaining. Right now, from your limited code block (the first one), it appears you might not be doing that. You want something like "[_words removeObjectAtIndex:letterIndex]" and you would also want to iterate from number of letters down to zero as you remove items from the array also: for (int i=[_words count]; i > [_words count]; i--) because you need to go from 4 letters down to 0 letters left.
So, I'm sure there are more efficient ways to do this, but I go by the rule of not optimizing until you need to. With that in mind, this code appears to work correctly:
- (NSString *)scrambleWord:(NSString *)word {
NSMutableArray *letterArray = [self letterArrayFromWord:word];
NSMutableString *returnValue = [[NSMutableString alloc] init];
do {
int randomIndex = arc4random() % letterArray.count;
[returnValue appendString:letterArray[randomIndex]];
[letterArray removeObjectAtIndex:randomIndex];
if (letterArray.count == 1) {
[returnValue appendString:letterArray[0]];
break;
}
} while (YES);
if ([[returnValue copy] isEqualToString:word]) {
return [self scrambleWord:word];
} else {
return [returnValue copy];
}
}
- (NSMutableArray *)letterArrayFromWord:(NSString *)word {
NSMutableArray *array = [NSMutableArray array];
for (int i = 0; i < word.length; i = i + 1) {
[array addObject:[NSString stringWithFormat:#"%C", [word characterAtIndex:i]]];
}
return array;
}

iOS - Stanford CS193P Fall 2011 course, Assignment 2 descriptionOfProgram issue

Part of this assignment includes printing out on the display the current equation that is present to be solved, for that I use the following methods:
+ (NSString *)descriptionOfTopOfStack:(NSMutableArray *)stack {
NSMutableString *programFragment = [NSMutableString stringWithString:#""];
id topOfStack = [stack lastObject];
if (topOfStack) [stack removeLastObject];
if ([topOfStack isKindOfClass:[NSNumber class]]) {
[programFragment appendFormat:#"%g", [topOfStack doubleValue]];
} else if ([topOfStack isKindOfClass:[NSString class]]) {
NSString *operation = topOfStack;
if ([self isDoubleOperandOperation:operation]) {
[programFragment appendFormat:#"(%# %# %#)", [self descriptionOfTopOfStack:stack], operation, [self descriptionOfTopOfStack:stack]];
} else if ([self isSingleOperandOperation:operation]) {
[programFragment appendFormat:#"%#( %# )", operation, [self descriptionOfTopOfStack:stack]];
} else if ([ self isNoOperandOperation:operation]) {
[programFragment appendFormat:#"%#", operation];
} else if ([self isVariable:operation]) {
[programFragment appendFormat:#"%#", operation];
}
}
return programFragment;
}
+ (NSString *)descriptionOfProgram:(id)program {
NSMutableArray *stack;
if ([program isKindOfClass:[NSArray class]]) {
stack = [program mutableCopy];
}
return [self descriptionOfTopOfStack:stack];
}
My program computes the results and everything just fine, the only problem is that when I enter a variable, digit or single operand operation the display only shows said last entry, because it doesn't continue to iterate over the rest of the values present in the array, because no other recursive calls are made, any idea how I can make the program execute throughout the entire stack and not have it break the output?
I am not quite sure what you mean. The recursion should stop at a variable, digit or single operand operation. Although for a sin(operand) operation it should continue with the operand.
Did you take into account that your stack might be not completely defined?
Say you enter: 3 Enter 5 + 6 Enter 7 * 9 sqrt
this should translate to: 3+5, 6, sqrt(7*9)
So you have three elements still on your stack, but your approach stopped at sqrt(7*9).
You need to add a check at the to see if there is anything left on the stack, and continue if necessary (and add the comma's).
OK, another hint then (to be added at the end):
if ([stack count]) { // did I finish the entire stack?
[programFragment appendFormat:#"%#, %#", [self describeStack:stack], programFragment];
}
Interestingly you have used a NSMutableString, I did it with a NSString and used the class method stringWithFormat. So each time my result is a new string. I do not know if either approach is better.
aleene already answered, but just to clarify. I added the [stack count] check in the method that calls the recursive function.
+ (NSString *)descriptionOfProgram:(id)program {
NSMutableArray *stack;
NSString *strDesc = #"";
if ([program isKindOfClass:[NSArray class]]) {
// Make a consumable, mutable copy:
stack = [program mutableCopy];
}
while (stack.count) {
strDesc = [strDesc stringByAppendingString:[self descriptionOfTopOfStack:stack]];
if (stack.count) {
// More statements still on stack. We will loop again, but first, append comma separator:
strDesc = [strDesc stringByAppendingString:#", "];
}
}
return strDesc;
}

Resources