Swift Code Exercises
I’ve updated some of my programming tutorial exercises with Swift code samples. See the original exercise for complete problem description. Swift 2.1 code tested in Xcode 7.2 Playground.
The anagram problem
A string is an anagram of another string if re-arranging the letters results in the other string. “Astronomer” and “moon-starer!” are anagrams.
In the original problem we stripped all punctuation since symbols and spaces may be ignored. Let’s say we wanted to count “Ray.adverb” as a single word (for whatever reason) including the period, but “ray-adverb” should still ignore the hyphen as normal. In the code below we strip by word boundaries.
import Cocoa
var str1 = "Astronomer!"
var str2 = " Moon starer?"
var str3 = "ray-adverb"
var str4 = "barry-dave."
var str5 = "Ray.Adverb"
var str6 = "Tom Marvolo-Riddle"
var str7 = "I am Lord Voldemort!"
func areAnagrams(phrase1: String, phrase2: String) -> Bool
{
if phrase1.isEmpty { return false }
if phrase2.isEmpty { return false }
//var trimmedPhrase1 = phrase1.stringByReplacingOccurrencesOfString(" ", withString: "")
//var trimmedPrhase2 = phrase2.stringByReplacingOccurrencesOfString(" ", withString: "")
var trimmedPhrase1 = stripWordBoundaries(phrase1)
var trimmedPrhase2 = stripWordBoundaries(phrase2)
trimmedPhrase1 = trimmedPhrase1.lowercaseString
trimmedPrhase2 = trimmedPrhase2.lowercaseString
if trimmedPrhase2 == trimmedPhrase1 {
return false
}
if trimmedPrhase2.characters.count != trimmedPhrase1.characters.count {
return false
}
let chars1 = trimmedPhrase1.characters.sort()
let chars2 = trimmedPrhase2.characters.sort()
if chars1 == chars2 {
return true
}
return false
}
// let's treat "Ray.adverb" as a word so period would not be stripped, but "Adverb-Ray?" would become AdverbRay
func stripWordBoundaries(string: String) -> String
{
var words : [String] = []
string.enumerateSubstringsInRange(string.characters.indices,
options: .ByWords) {
(substring, _, _, _) -> () in
words.append(substring!)
}
return words.joinWithSeparator("")
}
func anagramTest(phrase1 ph1: String, phrase2 ph2: String)
{
if areAnagrams(ph1, phrase2: ph2) {
print("'\(ph1)' and '\(ph2)' are anagrams")
}
else {
print("'\(ph1)' and '\(ph2)' are NOT anagrams")
}
}
anagramTest(phrase1: str1, phrase2: str2)
anagramTest(phrase1: str3, phrase2: str4)
anagramTest(phrase1: str5, phrase2: str4)
anagramTest(phrase1: str6, phrase2: str7)
The sum-zero triplet problem
We want to check if a set of integers contains three integers that sum to zero. See original problem for complete description and alternate solutions.
import Cocoa
// test numbers
let numArray1 = [ 0, 5, -2, 2, -3, 42, 10 ];
let numArray2 = [ 1, 4, 0, 7, 3 ];
let numArray3 = [ 10, 4, -4, 8, 0 ];
let testArray = [ -2, -1, -3, 4, -5, 6 ];
// return an optional tupple; returns nil of none exist
//
func findTripletSumZero(inIntegers intArray: [Int]) -> (Int, Int, Int)?
{
if intArray.count < 3 {
return nil
}
let sortedArray = intArray.sort()
// all positives never sum zero
if sortedArray[0] > 0 {
return nil
}
// check from each end and adjust according to sorted order
for i in 0 ..< sortedArray.count
{
var left = i + 1;
var right = sortedArray.count - 1
// squeeze until right crosses paths with left
while right > left
{
let sum = sortedArray[i] + sortedArray[left] + sortedArray[right]
if sum == 0
{
return (sortedArray[i], sortedArray[left], sortedArray[right])
}
else if sum > 0 {
right -= 1
}
else {
left += 1
}
}
}
return nil
}
func performTest(inIntegers intArray: [Int])
{
let test = findTripletSumZero(inIntegers: intArray)
if (test == nil) {
print("Array \(intArray) has no triplet which sum zero.")
}
else {
let firstInt = test!.0
let secondInt = test!.1
let thirdInt = test!.2
print("Array \(intArray) has zero-sum triplet: (\(firstInt),\(secondInt),\(thirdInt)).")
}
}
performTest(inIntegers: testArray)
performTest(inIntegers: numArray1)
performTest(inIntegers: numArray2)
performTest(inIntegers: numArray3)
In Swift 2, a String is not a sequence type so we use String.CharacterView (astring.characters). There are a number of ways to loop through a CharacterView, but since we want to compare characters at two indices then let’s convert to an Array. See the original problem for details.
import Cocoa
func phraseIsPalindrome(phrase: String) -> Bool
{
let regex = try! NSRegularExpression(pattern: "[\\s\\p{P}]", options: [.CaseInsensitive])
let trimmedPhrase = regex.stringByReplacingMatchesInString(phrase, options:[], range: NSMakeRange(0, phrase.characters.count), withTemplate: "")
if trimmedPhrase.characters.count > 1 {
let charArray = Array(trimmedPhrase.lowercaseString.characters)
// go to mid-point while checking for same character on other end
for i in 0 ..< len / 2 + 1 {
if charArray[i] != charArray[charArray.count - i - 1] {
return false
}
}
return true
}
else if trimmedPhrase.characters.count == 1 {
// allow single-character phrase
return true
}
return false
}
phraseIsPalindrome("Never odd or even.") // true
phraseIsPalindrome("Never odd or evan") // false
phraseIsPalindrome("Madam, I'm Adam!") // true