Open andreiz opened 11 months ago
I asked ChatGPT 4 to write the initial version with this prompt:
Write a Swift program that loops through every line in a text file, and for each line finds the first and the last digit in it, and then combines the digits into a number
I then also asked:
Add detailed comments before every line. Assume I am a Swift newbie.
This was the result:
// Import the Foundation framework. This framework provides essential data types,
// collections, and operating system services.
import Foundation
// Define a function named 'findFirstAndLastDigit' which takes a string as input
// and returns an optional integer.
func findFirstAndLastDigit(in string: String) -> Int? {
// Use the 'filter' method to iterate over each character in the string,
// keeping only those that are numbers.
let digits = string.filter { $0.isNumber }
// Use 'guard' to check if the first and last characters in the 'digits' string exist.
// If they don't, the function will exit and return 'nil'.
guard let first = digits.first, let last = digits.last else { return nil }
// Combine the first and last digits into a new string, and then try to
// convert this string to an integer. The result is returned. If the
// conversion fails, it will return 'nil'.
return Int(String(first) + String(last))
}
// Define a function named 'processFile' that takes a file path as a string and
// doesn't return anything.
func processFile(atPath path: String) {
do {
// Try to read the content of the file at the specified path as a string.
// If this operation fails, the catch block will be executed.
let content = try String(contentsOfFile: path, encoding: .utf8)
// Split the content into an array of strings, each representing a line.
// The splitting is based on newline characters.
let lines = content.components(separatedBy: .newlines)
// Iterate through each line in the 'lines' array.
for line in lines {
// Call 'findFirstAndLastDigit' for the current line.
// If it finds digits, it will return an integer, otherwise nil.
if let number = findFirstAndLastDigit(in: line) {
// Print the line and the extracted number if digits are found.
print("Line: \(line) -> Number: \(number)")
} else {
// Print a message indicating no digits were found in the line.
print("Line: \(line) -> No digits found")
}
}
} catch {
// If there was an error in reading the file, print the error message.
print("Error reading file: \(error)")
}
}
// Define the file path as a string. Replace this with the actual path to your text file.
let filePath = "path/to/your/textfile.txt"
// Call the 'processFile' function with the specified file path to process the file.
processFile(atPath: filePath)
I wasn't clear on the .newlines
bit, so I asked ChatGPT to clarify. This was the answer:
In Swift,
.newlines
is a shorthand notation for accessing a static property of theCharacterSet
class. When .newlines is used in a context where aCharacterSet
is expected, Swift infers that it refers toCharacterSet.newlines
.This inference is based on the type context. Swift's type inference system allows you to write cleaner and more concise code by omitting the explicit type when it can be automatically inferred from the context.
In the code snippet
content.components(separatedBy: .newlines)
, thecomponents(separatedBy:)
method expects an argument of typeCharacterSet
. Since.newlines
is used in a parameter position where aCharacterSet
is expected, Swift automatically infers that.newlines
refers toCharacterSet.newlines
.
I added a couple of lines of code to keep track of the sum through the loop and then print it at the end.
Testing it on a sample 3 line file:
4three53pczsx1sevenmzmtrzz
four24qphdrxfsf
gdgj3f
The output was:
Line: 4three53pczsx1sevenmzmtrzz -> Number: 41
Line: four24qphdrxfsf -> Number: 24
Line: gdgj3f -> Number: 33
Line: -> No digits found
Sum: 98
Apparently, If the input file ends with a newline character, the components(separatedBy: .newlines)
method will interpret this as an extra, empty line. So we need to skip empty lines:
if line.isEmpty {
continue
}
Part 2 initial solution (just the updated function):
// Function to find the first and last digit (numeric or spelled-out) in a string
func findFirstAndLastDigit(in string: String) -> Int? {
// Regular expression pattern to match numeric digits and spelled-out digits
// The pattern looks for whole words that match either a single digit (\\d) or any of the spelled-out digits
let pattern = "\\b(zero|one|two|three|four|five|six|seven|eight|nine|\\d)\\b"
// Array to store all found digits
var digits = [Int]()
do {
// Create a regular expression object from the pattern
let regex = try NSRegularExpression(pattern: pattern)
// Find matches in the input string
// The matches method scans the string for matches to the pattern
let matches = regex.matches(in: string, range: NSRange(string.startIndex..., in: string))
// Process each match found in the string
for match in matches {
// Extract the range of each match
let matchRange = Range(match.range, in: string)!
// Extract the matched substring from the original string
let matchString = String(string[matchRange])
// Try to convert the matched string directly to a digit
if let digit = Int(matchString) {
digits.append(digit)
} else if let digit = digitMap[matchString] {
// If direct conversion fails, check if it's a spelled-out digit using the digitMap
digits.append(digit)
}
}
} catch {
print("Error creating regex: \(error)")
return nil
}
// Ensure that at least two digits were found
guard let first = digits.first, let last = digits.last else { return nil }
// Combine the first and last digits into a single integer
return Int(String(first) + String(last))
}
I see the regexp handling is not as concise as in some other languages.
The result of this was scored as too low by the advent checker. Probably because it doesn't handle cases where words overlap, like eighthree
.
Rewritten to use do 2 passes. First - extract numeric digits and their indices. Second - for each word in the digit map, iterate the input from start and locate all occurrences, adding to the array. Then sort by indices and pick first and last.
This passed
https://adventofcode.com/2023/day/1