Objective-C 练习题,题目来源 exercism.io,一共 50 题,见 GitHub

第五篇共 10 题,完。

(一)
(二)
(三)
(四)

# Pascals Triangle

题目和测试用例见链接,下同。Pascals Triangle
Compute Pascal’s triangle up to a given number of rows.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// PascalsTriangle.h
#import <Foundation/Foundation.h>

@interface PascalsTriangle : NSObject

@property (nonatomic, strong) NSArray *rows;

- (instancetype)initWithNumberOfRows:(NSInteger)rows;

@end

// PascalsTriangle.m
#import "PascalsTriangle.h"

@implementation PascalsTriangle

- (instancetype)initWithNumberOfRows:(NSInteger)rows {
if (self = [super init]) {
NSMutableArray *rowsArray = [NSMutableArray array];
for (NSInteger i = 0; i < rows; ++i) {
if (i == 0) {
[rowsArray addObject:@[@1]];
} else if (i == 1) {
[rowsArray addObject:@[@1, @1]];
} else {
NSMutableArray *array = [NSMutableArray arrayWithObject:@1];
NSArray *lastRowArray = [rowsArray lastObject];
for (NSInteger j = 1; j < [lastRowArray count]; ++j) {
[array addObject:@([lastRowArray[j - 1] integerValue] + [lastRowArray[j] integerValue])];
}
[array addObject:@1];
[rowsArray addObject:array];
}
}
self.rows = rowsArray;
}
return self;
}

@end

# Say

Say
Given a number from 0 to 999,999,999,999, spell out that number in English.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
// Say.h
#import <Foundation/Foundation.h>

@interface Say : NSObject

+ (NSString *)say:(long)number;

@end

// Say.m
#import "Say.h"

@implementation Say

+ (NSString *)say:(long)number {
if (number < 0 || number >= 1000000000000) {
return nil;
}
if (number == 0) {
return @"zero";
}
NSMutableString *string = [NSMutableString string];
if (number / 1000000000 > 0) {
[string appendFormat:@"%@ billion", [self sayLessThanOneThousand:number / 1000000000]];
number -= (number / 1000000000 * 1000000000);
}
if (number / 1000000 > 0) {
[string appendFormat:@" %@ million", [self sayLessThanOneThousand:number / 1000000]];
number -= (number / 1000000 * 1000000);
}
if (number / 1000 > 0) {
[string appendFormat:@" %@ thousand", [self sayLessThanOneThousand:number / 1000]];
number -= (number / 1000 * 1000);
}
[string appendFormat:@" %@", [self sayLessThanOneThousand:number]];
[string replaceOccurrencesOfString:@" " withString:@" " options:NSCaseInsensitiveSearch range:NSMakeRange(0, string.length)];
return [string stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@" "]];
}

+ (NSString *)sayLessThanOneThousand:(NSInteger)number {
NSMutableString *string = [NSMutableString string];
if (number > 99) {
[string appendFormat:@" %@ hundred", dict()[@(number / 100)]];
number -= (number / 100 * 100);
}
if (number > 19) {
[string appendFormat:@" %@", dict()[@(number / 10 * 10)]];
if (number % 10 > 0) {
[string appendFormat:@"-%@", dict()[@(number % 10)]];
}
} else {
if (number != 0) {
[string appendFormat:@" %@", dict()[@(number)]];
}
}
return string;
}

NSDictionary *dict(void) {
return @{
@1 : @"one",
@2 : @"two",
@3 : @"three",
@4 : @"four",
@5 : @"five",
@6 : @"six",
@7 : @"seven",
@8 : @"eight",
@9 : @"nine",
@10 : @"ten",
@11 : @"eleven",
@12 : @"twelve",
@13 : @"thirteen",
@14 : @"fourteen",
@15 : @"fifteen",
@16 : @"sixteen",
@17 : @"seventeen",
@18 : @"eighteen",
@19 : @"nineteen",
@20 : @"twenty",
@30 : @"thirty",
@40 : @"forty",
@50 : @"fifty",
@60 : @"sixty",
@70 : @"seventy",
@80 : @"eighty",
@90 : @"ninety",
};
}

@end

# Bracket Push

Bracket Push
Make sure the brackets and braces all match.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// BracketPushExample.h
#import <Foundation/Foundation.h>

@interface BracketPushExample : NSObject

+ (BOOL)validateBracketPairingAndNestingInString:(NSString *)string;

@end

// BracketPushExample.m
#import "BracketPushExample.h"

@implementation BracketPushExample

+ (BOOL)validateBracketPairingAndNestingInString:(NSString *)string {
__block NSMutableArray *array = [NSMutableArray array];
__block BOOL result = YES;
[string enumerateSubstringsInRange:NSMakeRange(0, string.length) options:NSStringEnumerationByComposedCharacterSequences usingBlock:^(NSString * _Nullable substring, NSRange substringRange, NSRange enclosingRange, BOOL * _Nonnull stop) {
if ([substring isEqualToString:@"["]
|| [substring isEqualToString:@"{"]
|| [substring isEqualToString:@"("]) {
[array addObject:substring];
}
if ([substring isEqualToString:@"]"]
|| [substring isEqualToString:@"}"]
|| [substring isEqualToString:@")"]) {
if (([substring isEqualToString:@"]"] && [[array lastObject] isEqualToString:@"["])
|| ([substring isEqualToString:@"}"] && [[array lastObject] isEqualToString:@"{"])
|| ([substring isEqualToString:@")"] && [[array lastObject] isEqualToString:@"("])) {
[array removeLastObject];
} else {
result = NO;
*stop = YES;
}
}
}];
return result && (array.count == 0);
}

@end

# Roman Numerals

Roman Numerals
Write a function to convert from normal numbers to Roman Numerals.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
// RomanNumerals.h
#import <Foundation/Foundation.h>

@interface RomanNumerals : NSObject

+ (NSString *)romanNumeralsForValue:(NSInteger)value;

@end

// RomanNumerals.m
#import "RomanNumerals.h"

@implementation RomanNumerals

+ (NSString *)romanNumeralsForValue:(NSInteger)value {
if (value < 1 || value >= 4000) {
return @"";
}
NSMutableString *string = [NSMutableString string];
NSArray *array = @[ @"M", @1000,
@"D", @500,
@"C", @100,
@"L", @50,
@"X", @10,
@"V", @5,
@"I", @1 ];
NSInteger index = 0;
while (value > 0) {
NSInteger indexValue = [array[index + 1] integerValue];
if (value / indexValue > 0) {
if (value / indexValue == 4) {
[string appendFormat:@"%@%@", array[index], array[index - 2]];
value -= (value / indexValue * indexValue);
} else if (index + 3 < array.count && value / [array[index + 3] integerValue] == 9) {
[string appendFormat:@"%@%@", array[index + 2], array[index - 2]];
value -= (value / [array[index + 3] integerValue] * [array[index + 3] integerValue]);
} else {
for (NSInteger i = 0; i < value / indexValue; ++i) {
[string appendString:array[index]];
}
value -= (value / indexValue * indexValue);
}
}
index += 2;
}
return string;
}

@end

# Strain

Strain
Implement the keep and discard operation on collections. Given a collection and a predicate on the collection’s elements, keep returns a new collection containing those elements where the predicate is true, while discard returns a new collection containing those elements where the predicate is false.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// Strain.h
#import <Foundation/Foundation.h>

@interface NSArray (Strain)

- (NSArray *)keep:(BOOL)isKeep where:(BOOL(^)(id input))condition;
- (NSArray *)discard:(BOOL(^)(id input))condition;

@end

// Strain.m
#import "Strain.h"

@implementation NSArray (Strain)

- (NSArray *)keep:(BOOL)isKeep where:(BOOL(^)(id input))condition {
__block NSMutableArray *array = [NSMutableArray array];
[self enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
if (condition(obj) && isKeep) {
[array addObject:obj];
}
}];
return array;
}

- (NSArray *)discard:(BOOL(^)(id input))condition {
__block NSMutableArray *array = [NSMutableArray array];
[self enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
if (!condition(obj)) {
[array addObject:obj];
}
}];
return array;
}

@end

# Run Length Encoding

Run Length Encoding
Implement run-length encoding and decoding.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
// RunLengthEncoding.h
#import <Foundation/Foundation.h>

@interface RunLengthEncoding : NSObject

+ (NSString *)encode:(NSString *)input;
+ (NSString *)decode:(NSString *)input;

@end

// RunLengthEncoding.m
#import "RunLengthEncoding.h"

@implementation RunLengthEncoding

+ (NSString *)encode:(NSString *)input {
__block NSMutableString *result = [NSMutableString string];
__block NSString *string = @"";
__block NSInteger repeatCount = 0;
[input enumerateSubstringsInRange:NSMakeRange(0, input.length) options:NSStringEnumerationByComposedCharacterSequences usingBlock:^(NSString * _Nullable substring, NSRange substringRange, NSRange enclosingRange, BOOL * _Nonnull stop) {
if (substringRange.location == 0 || [string isEqualToString:substring]) {
repeatCount++;
} else {
repeatCount == 1 ? [result appendString:string] : [result appendFormat:@"%ld%@", (long)repeatCount, string];
repeatCount = 1;
}
string = substring;
if (substringRange.location == input.length - 1) {
repeatCount == 1 ? [result appendString:string] : [result appendFormat:@"%ld%@", (long)repeatCount, string];
}
}];
return result;
}

+ (NSString *)decode:(NSString *)input {
__block NSString *number = @"";
__block NSMutableString *result = [NSMutableString string];
[input enumerateSubstringsInRange:NSMakeRange(0, input.length) options:NSStringEnumerationByComposedCharacterSequences usingBlock:^(NSString * _Nullable substring, NSRange substringRange, NSRange enclosingRange, BOOL * _Nonnull stop) {
if (isdigit([substring characterAtIndex:0])) {
number = [number stringByAppendingString:substring];
} else {
if (number.length > 0) {
for (NSInteger i = 0; i < [number integerValue]; ++i) {
[result appendString:substring];
}
} else {
[result appendString:substring];
}
number = @"";
}
}];
return result;
}

@end

# Luhn

Luhn
Given a number determine whether or not it is valid per the Luhn formula.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
// Luhn.h
#import <Foundation/Foundation.h>

@interface Luhn : NSObject

@property (nonatomic, assign) BOOL isValid;

- (instancetype)initWithString:(NSString *)input;

@end

// Luhn.m
#import "Luhn.h"

@implementation Luhn

- (instancetype)initWithString:(NSString *)input {
if (self = [super init]) {
if ([[NSPredicate predicateWithFormat:@"SELF MATCHES %@", @"^[0-9 ]{1,}$"] evaluateWithObject:input]) {
input = [input stringByReplacingOccurrencesOfString:@" " withString:@""];
if (input.length <= 1) {
self.isValid = false;
} else {
__block NSInteger sum = 0;
[input enumerateSubstringsInRange:NSMakeRange(0, input.length) options:NSStringEnumerationByComposedCharacterSequences usingBlock:^(NSString * _Nullable substring, NSRange substringRange, NSRange enclosingRange, BOOL * _Nonnull stop) {
if ((input.length % 2 == 0 && substringRange.location % 2 == 0)
|| (input.length % 2 == 1 && substringRange.location % 2 == 1)) {
if ([substring integerValue] * 2 > 9) {
sum += ([substring integerValue] * 2 - 9);
} else {
sum += ([substring integerValue] * 2);
}
} else {
sum += [substring integerValue];
}
}];
self.isValid = sum % 10 == 0 ? YES : NO;
}
} else {
self.isValid = NO;
}
}
return self;
}

@end

# Rna Transcription

Rna Transcription
Given a DNA strand, return its RNA Complement Transcription.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// RNATranscription.h
#import <Foundation/Foundation.h>

@interface RNATranscription : NSObject

- (NSString *)rnaFromDNAStrand:(NSString *)DNAStrand;

@end

// RNATranscription.m
#import "RNATranscription.h"

@implementation RNATranscription

- (NSString *)rnaFromDNAStrand:(NSString *)DNAStrand {
if ([[NSPredicate predicateWithFormat:@"SELF MATCHES %@", @"^[ACGT]{1,}$"] evaluateWithObject:DNAStrand]) {
NSMutableString *result = [NSMutableString string];
[DNAStrand enumerateSubstringsInRange:NSMakeRange(0, DNAStrand.length) options:NSStringEnumerationByComposedCharacterSequences usingBlock:^(NSString * _Nullable substring, NSRange substringRange, NSRange enclosingRange, BOOL * _Nonnull stop) {
if ([substring isEqualToString:@"A"]) {
[result appendString:@"U"];
}
if ([substring isEqualToString:@"C"]) {
[result appendString:@"G"];
}
if ([substring isEqualToString:@"G"]) {
[result appendString:@"C"];
}
if ([substring isEqualToString:@"T"]) {
[result appendString:@"A"];
}
}];
return result;
} else {
return nil;
}
}

@end

# Grains

Grains
Calculate the number of grains of wheat on a chessboard given that the number on each square doubles.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// Grains.h
#import <Foundation/Foundation.h>

@interface Grains : NSObject

- (unsigned long long)grainsAtSquareNumber:(NSInteger)number;
- (unsigned long long)grainsAtBoard;

@end

// Grains.m
#import "Grains.h"

@implementation Grains

- (unsigned long long)grainsAtSquareNumber:(NSInteger)number {
if (number <= 0 || number > 64) {
return -1;
}
return exp2(number - 1);
}

- (unsigned long long)grainsAtBoard {
return [self grainsAtSquareNumber:64] * 2 - 1;
}

@end

# Nth Prime

Nth Prime
Given a number n, determine what the nth prime is.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
// NthPrime.h
#import <Foundation/Foundation.h>

@interface NthPrime : NSObject

+ (int)primeNum:(int)number;

@end

// NthPrime.m
#import "NthPrime.h"

@implementation NthPrime

+ (int)primeNum:(int)number {
if (number == 0) {
return 0;
}
if (number == 1) {
return 2;
}
if (number == 2) {
return 3;
}
int index = 2;
int result = 0;
for (int i = 5; ; i += 2) {
BOOL isPrime = YES;
for (int j = 3; j < i; j +=2) {
if (i % j == 0) {
isPrime = NO;
break;
}
}
if (isPrime) {
result = i;
index++;
if (index == number) {
break;
}
}
}
return result;
}

@end