More Problems with Object Designs

xcode.jpg

This morning I was playing around with the CryptoQuip solver, and realizing that the mapping code I had could be made a little simpler if I used the fact that ASCII character representations are all numerically ascending, and that makes character math something to use - as opposed to simple look-ups. For instance, if we look at the existing code for +createPatternText::

  1. + (NSString*) createPatternText:(NSString*)text
  2. {
  3. const char *src = [[text lowercaseString] UTF8String];
  4. NSUInteger len = [text length];
  5. const char *ascii = "abcdefghijklmnopqrstuvwxyz";
  6. char pattern[255];
  7. for (NSUInteger i = 0; i < len; ++i) {
  8. pattern[i] = ascii[strchr(src, src[i]) - src];
  9. }
  10. pattern[len] = '\0';
  11. return [NSString stringWithUTF8String:pattern];
  12. }

the look-up on line 8, using the const char array defined on line 5, is really just an offset calculation, and can be replaced with:

  1. + (NSString*) createPatternText:(NSString*)text
  2. {
  3. const char *src = [[text lowercaseString] UTF8String];
  4. NSUInteger len = MIN([text length], 255);
  5. char pattern[255];
  6. for (NSUInteger i = 0; i < len; ++i) {
  7. pattern[i] = 'a' + (strchr(src, src[i]) - src);
  8. }
  9. pattern[len] = '\0';
  10. return [NSString stringWithUTF8String:pattern];
  11. }

and the result is exactly the same. But now, we're not creating the const char array on the stack, on each call, and the offset into the array is the same addition as we are doing here. Simpler.

But when we go to Swift, we see that the beauty and elegance of the underlying data is being clouded with the all-too-common object oriented design of Character.

  1. var pattern: String {
  2. get {
  3. let ascii: [Character] = ["a","b","c","d","e","f","g","h","i","j","k",
  4. "l","m","n","o","p","q","r","s","t","u","v",
  5. "w","x","y","z"]
  6. var ans = ""
  7. let src = Array(self.lowercased().utf8)
  8. for c in src {
  9. ans.append(ascii[src.firstIndex(of: c)!])
  10. }
  11. return ans
  12. }
  13. }

Sadly, we can't simplify line 9 because the Character object doesn't allow for construction based on an ASCII value. Sure, it has all kinds of nice interrogation methods to test and see if it's ASCII, or uppercase, or a number... but you can't actually do the simple math of adding 1 to 'a' and getting 'b'. That is a shame, and it's not something you can easily add because the Character class would need a new -init: method.

So while I really like parts of Swift 5, I think they didn't really think about all the uses that ObjC fits into when making the new classes. It's a process, I get that, but it's causing folks to write code around these issues, and that's a mistake. No one will go back and fix their code when, and if, they add a way to do this.