diff --git a/concepts/for-loops/.meta/config.json b/concepts/for-loops/.meta/config.json index d5cb6a257..1d255acef 100644 --- a/concepts/for-loops/.meta/config.json +++ b/concepts/for-loops/.meta/config.json @@ -1,7 +1,8 @@ { "blurb": "For loops can be used to iterate over a sequence of values.", "authors": [ - "wneumann" + "wneumann", + "meatball133" ], "contributors": [] } diff --git a/concepts/for-loops/about.md b/concepts/for-loops/about.md index 32c347589..7a3685622 100644 --- a/concepts/for-loops/about.md +++ b/concepts/for-loops/about.md @@ -1,84 +1,121 @@ -# About +# For loops -[For-in loops][for-in-loops] are used to iterate over a sequence of values, taking each element in turn, binding it to a variable or constant name of the developer's choosing, then executes a block of code that may refer to the element. When every element of the sequence has been iterated over, the loop exits and execution begins with the first line following the body of the loop. +Looping is a fundamental concept in programming that allows you to execute a block of code multiple times. +In Swift, there are two types of loops: [`for-in` loops][for-loops] and `while` loops. +In this chapter, you'll learn about `for-in` loops. + +For loops allows you to iterate over a sequence of values, taking each element in turn, and binding it to a variable of your choosing. +Swift allows you to iterate over a variety of sequences, such as ranges, arrays, and strings (and more types which will be covered later). +When every element of the sequence has been iterated over, the loop exits. + +For loops are declared by using the `for` keyword, followed by a variable name, the `in` keyword, and a sequence of values to iterate over. +The variable given in the `for-in` loop is inmutable, meaning you can't change its value inside the loop. +Here's an example of a `for-in` loop that iterates over an array of numbers: ```swift let numbers = [3, 10, 7, 11] -let word = "Supercalifragilisticexpialidocious" for number in numbers { - print("\(number) / 2 = \(number / 2)") + print(number) } print("Done with numbers") // prints: -// 3 / 2 = 1 -// 10 / 2 = 5 -// 7 / 2 = 3 -// 11 / 2 = 5 +// 3 +// 10 +// 7 +// 11 // Done with numbers +``` +~~~~exercism/note +The `number` variable is declared in the `for-in` loop and is only available within the loop's scope. -for char in word { - if "aeiou".contains(char) { - print(char, terminator: "") - } -} -print(" - those are all the vowels") - -// prints: -// ueaiaiiieiaioiou - those are all the vowels +```swift +let numbers = [3, 10, 7, 11] +for number in numbers { + number + 1 +} +number + 1 +// Error: Use of unresolved identifier 'number' ``` +~~~~ + +## Iterating over a range -If one needs to mutate the current element of the iteration, it can be declared as a variable in the for-in loop: +You can also iterate over a range of numbers using a `for-in` loop. +This allows you to execute a block of code a specific number of times, for example, the range `1...5` will iterate over the numbers 1, 2, 3, 4, and 5, so the loop will execute 5 times. +Sometimes you might want to iterate over indexes, in a datastructure like an array, then you can use a `0.. 0 { - print(x % 10) - x /= 10 - } - print() +let numbers = [3, 10, 7, 11] + +for i in 0.. Int { + var count = 0 + for day in birdsPerDay { + count += day + } + return count +} + +func birdsInWeek(_ birdsPerDay: [Int], weekNumber: Int) -> Int { + var count = 0 + let startOfWeek = (weekNumber - 1) * 7 + let endOfWeek = startOfWeek + 7 + for i in startOfWeek.. [Int] { + var result = [Int](birdsPerDay) + for i in stride(from: 0, through: birdsPerDay.count - 1, by: 2) { + result[i] += 1 + } + return result +} \ No newline at end of file diff --git a/exercises/concept/bird-watcher/.meta/config.json b/exercises/concept/bird-watcher/.meta/config.json new file mode 100644 index 000000000..bcf1eff1b --- /dev/null +++ b/exercises/concept/bird-watcher/.meta/config.json @@ -0,0 +1,20 @@ +{ + "authors": [ + "meatball133" + ], + "files": { + "solution": [ + "Sources/BirdWatcher/BirdWatcher.swift" + ], + "test": [ + "Tests/BirdWatcherTests/BirdWatcherTests.swift" + ], + "exemplar": [ + ".meta/Sources/BirdWatcher/BirdWatcherExemplar.swift" + ] + }, + "forked_from": [ + "go/bird-watcher" + ], + "blurb": "Count the birds in your garden with for loops." +} diff --git a/exercises/concept/bird-watcher/.meta/design.md b/exercises/concept/bird-watcher/.meta/design.md new file mode 100644 index 000000000..07f490d2f --- /dev/null +++ b/exercises/concept/bird-watcher/.meta/design.md @@ -0,0 +1,31 @@ +# Design + +This concept exercise should convey a basic understanding of how to work with closures in Swift. + +## Learning objectives + +- Defining closures +- Assigning closures to names +- Capturing the environment +- Shorthand argument names +- Trailing closure syntax + +## Out of scope + +- Capture lists +- Weak/Unowned +- Escaping closures +- Autoclosures + +## Concepts + +- `closures`: Know how to define closures; know how to assign closures to names +- `capturing`: know about environment capture +- `trailing-closures`: know about trailing closure syntax +- `shorthand-arguments`: know about shorthand closure syntax + +## Prerequisites + +- `functions` +- `higher-order-functions` +- `loops` diff --git a/exercises/concept/bird-watcher/Package.swift b/exercises/concept/bird-watcher/Package.swift new file mode 100644 index 000000000..25472ce29 --- /dev/null +++ b/exercises/concept/bird-watcher/Package.swift @@ -0,0 +1,28 @@ +// swift-tools-version:6.0 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let package = Package( + name: "BirdWatcher", + products: [ + // Products define the executables and libraries a package produces, and make them visible to other packages. + .library( + name: "BirdWatcher", + targets: ["BirdWatcher"]) + ], + dependencies: [ + // Dependencies declare other packages that this package depends on. + // .package(url: /* package url */, from: "1.0.0"), + ], + targets: [ + // Targets are the basic building blocks of a package. A target can define a module or a test suite. + // Targets can depend on other targets in this package, and on products in packages this package depends on. + .target( + name: "BirdWatcher", + dependencies: []), + .testTarget( + name: "BirdWatcherTests", + dependencies: ["BirdWatcher"]), + ] +) diff --git a/exercises/concept/bird-watcher/Sources/BirdWatcher/BirdWatcher.swift b/exercises/concept/bird-watcher/Sources/BirdWatcher/BirdWatcher.swift new file mode 100644 index 000000000..e84c68d84 --- /dev/null +++ b/exercises/concept/bird-watcher/Sources/BirdWatcher/BirdWatcher.swift @@ -0,0 +1,11 @@ +func totalBirdCount(_ birdsPerDay: [Int]) -> Int { + fatalError("Please implement the totalBirdCount(_:) function") +} + +func birdsInWeek(_ birdsPerDay: [Int], weekNumber: Int) -> Int { + fatalError("Please implement the birdsInWeek(_:weekNumber:) function") +} + +func fixBirdCountLog(_ birdsPerDay: [Int]) -> [Int] { + fatalError("Please implement the birdsInWeek(_:) function") +} diff --git a/exercises/concept/bird-watcher/Tests/BirdWatcherTests/BirdWatcherTests.swift b/exercises/concept/bird-watcher/Tests/BirdWatcherTests/BirdWatcherTests.swift new file mode 100644 index 000000000..aa2b68f08 --- /dev/null +++ b/exercises/concept/bird-watcher/Tests/BirdWatcherTests/BirdWatcherTests.swift @@ -0,0 +1,82 @@ +import Foundation +import Testing + +@testable import BirdWatcher + +let RUNALL = Bool(ProcessInfo.processInfo.environment["RUNALL", default: "false"]) ?? false + +@Suite struct BirdWatcherTests { + @Test("calculates the correct total number of birds") + func testTotalBirdCount() { + let birdCount = [9, 0, 8, 4, 5, 1, 3] + let expected = 30 + #expect(totalBirdCount(birdCount) == expected) + } + + @Test("works for a short bird count list", .enabled(if: RUNALL)) + func testTotalBirdCountShort() { + let birdCount = [2] + let expected = 2 + #expect(totalBirdCount(birdCount) == expected) + } + + @Test("works for a long bird count list", .enabled(if: RUNALL)) + func testTotalBirdCountLong() { + let birdCount = [2, 8, 4, 1, 3, 5, 0, 4, 1, 6, 0, 3, 0, 1, 5, 4, 1, 1, 2, 6] + let expected = 57 + #expect(totalBirdCount(birdCount) == expected) + } + + @Test("calculates the number of birds in the first week", .enabled(if: RUNALL)) + func testBirdsInFirstWeek() { + let birdCount = [3, 0, 5, 1, 0, 4, 1, 0, 3, 4, 3, 0, 8, 0] + let weekNumber = 1 + let expected = 14 + #expect(birdsInWeek(birdCount, weekNumber: weekNumber) == expected) + } + + @Test("calculates the number of birds for a week in the middle of the log", .enabled(if: RUNALL)) + func testBirdsInMiddleWeek() { + let birdCount = [4, 7, 3, 2, 1, 1, 2, 0, 2, 3, 2, 7, 1, 3, 0, 6, 5, 3, 7, 2, 3] + let weekNumber = 2 + let expected = 18 + #expect(birdsInWeek(birdCount, weekNumber: weekNumber) == expected) + } + + @Test("calculates the number of birds for a week in the end of the log", .enabled(if: RUNALL)) + func testBirdsInLastWeek() { + let birdCount = [4, 7, 3, 2, 1, 1, 2, 0, 2, 3, 2, 7, 1, 3, 0, 6, 5, 3, 7, 2, 3] + let weekNumber = 3 + let expected = 26 + #expect(birdsInWeek(birdCount, weekNumber: weekNumber) == expected) + } + + @Test("works when there is only one week", .enabled(if: RUNALL)) + func testBirdsInOneWeek() { + let birdCount = [3, 0, 3, 3, 2, 1, 0] + let weekNumber = 1 + let expected = 12 + #expect(birdsInWeek(birdCount, weekNumber: weekNumber) == expected) + } + + @Test("returns a bird count list with the corrected values", .enabled(if: RUNALL)) + func testFixBirdCountLog() { + var birdCount = [3, 0, 5, 1, 0, 4, 1, 0, 3, 4, 3, 0] + let expected = [4, 0, 6, 1, 1, 4, 2, 0, 4, 4, 4, 0] + #expect(fixBirdCountLog(birdCount) == expected) + } + + @Test("works for a short bird count list", .enabled(if: RUNALL)) + func testFixBirdCountLogShort() { + var birdCount = [4, 2] + let expected = [5, 2] + #expect(fixBirdCountLog(birdCount) == expected) + } + + @Test("works for a long bird count list", .enabled(if: RUNALL)) + func testFixBirdCountLogLong() { + var birdCount = [2, 8, 4, 1, 3, 5, 0, 4, 1, 6, 0, 3, 0, 1, 5, 4, 1, 1, 2, 6] + let expected = [3, 8, 5, 1, 4, 5, 1, 4, 2, 6, 1, 3, 1, 1, 6, 4, 2, 1, 3, 6] + #expect(fixBirdCountLog(birdCount) == expected) + } +}