Skip to content

[Swift 6]: Refactor Poetry Club exercise #844

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions concepts/importing/about.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
# About

While Swift includes a lot of functionality in its Standard Library, there is much more functionality available in the form of external libraries. These libraries may be from the Swift project itself, such as the [Swift Argument Parser][argument-parser], closed source libraries from companies Apple's [Network Framework][network-framework], or third-party libraries, like [Swifty Beaver][swifty-beaver].
While Swift includes a lot of functionality in its Standard Library, there is much more functionality available in the form of external libraries.
These libraries may be from the Swift project itself, such as the [Swift Argument Parser][argument-parser], closed source libraries from companies Apple's [Network Framework][network-framework], or third-party libraries, like [Swifty Beaver][swifty-beaver].

Some of these modules, like the Network Framework or [Foundation][apple-foundation] (which is probably the most commonly used library in Swift) come with your Swift distribution. External third party libraries need to be added to your project before they can be imported, though that is out of the scope of this exercise.
Some of these modules, like the Network Framework or [Foundation][apple-foundation] (which is probably the most commonly used library in Swift) come with your Swift distribution.
External third party libraries need to be added to your project before they can be imported, though that is out of the scope of this exercise.

Importing modules is done by writing the `import` keyword followed by the name of the module. So one can import Foundation by adding the following to their program.

Expand Down
21 changes: 16 additions & 5 deletions concepts/importing/introduction.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,22 @@
# Introduction

Importing modules is done by writing the `import` keyword followed by the name of the module. This allows access to all of the types, values, and functionality inside that module; in this example we are making use of the `components(separatedBy:)` method which becomes available to `String` with this import.
While Swift includes a lot of functionality in its Standard Library, there is much more functionality available in the form of external libraries.
These libraries may be from the Swift project itself, such as the [Swift Argument Parser][argument-parser], closed source libraries from companies Apple's [Network Framework][network-framework], or third-party libraries, like [Swifty Beaver][swifty-beaver].

Some of these modules, like the Network Framework or [Foundation][apple-foundation] (which is probably the most commonly used library in Swift) come with your Swift distribution.
External third party libraries need to be added to your project before they can be imported, though that is out of the scope of this exercise.

Importing modules is done by writing the `import` keyword followed by the name of the module. So one can import Foundation by adding the following to their program.

```swift
import Foundation

let csv = "apple,pear,peach,orange,cherry,lime,goosberry"
let fruit = csv.components(separatedBy: ",")
// => ["apple", "pear", "peach", "orange", "cherry", "lime", "goosberry"]
```

This allows access to all of the types, values, and functionality inside that module; for example if one wishes to use the `components(separatedBy:)` String method, that method becomes available to `String` with this import.

While they can be placed in the code anywhere before a pice of code that makes use of one the content of module, import statements are usually placed at the beginning of the file that uses them for greater readability.

[argument-parser]: https://apple.github.io/swift-argument-parser/documentation/argumentparser/
[network-framework]: https://developer.apple.com/documentation/network
[swifty-beaver]: https://github.com/SwiftyBeaver/SwiftyBeaver
[apple-foundation]: https://developer.apple.com/documentation/foundation
7 changes: 0 additions & 7 deletions concepts/string-components/.meta/config.json

This file was deleted.

46 changes: 0 additions & 46 deletions concepts/string-components/about.md

This file was deleted.

15 changes: 0 additions & 15 deletions concepts/string-components/introduction.md

This file was deleted.

1 change: 0 additions & 1 deletion concepts/string-components/links.json

This file was deleted.

4 changes: 3 additions & 1 deletion concepts/string-indexing/.meta/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,7 @@
"authors": [
"wneumann"
],
"contributors": []
"contributors": [
"meatball133"
]
}
66 changes: 57 additions & 9 deletions concepts/string-indexing/about.md
Original file line number Diff line number Diff line change
@@ -1,39 +1,87 @@
# About

Individual characters in a `String` can be accessed using the subscripting method used with arrays. However, the indices used for strings are _not_ integers and can not be worked with directly. Instead they must be computed based off of some other known index, such as `startIndex`, which points to the position of the first character in a nonempty string, using the methods available in the [`String`][string-docs] and [`NSString`][nsstring-docs] libraries (when the `Foundation` module is imported, strings in Swift have access to all of the NSString properties and methods).
Individual characters in a `String` can be accessed using the subscripting method used with arrays.
However, the [indices][string-indices] used for strings are _not_ integers and can not be worked with directly.
Instead they must be computed based off of some other known index, such as `startIndex`, which points to the position of the first character in a nonempty string, using the methods available in the [`String`][string-docs] and [`NSString`][nsstring-docs] libraries (when the `Foundation` module is imported, strings in Swift have access to all of the NSString properties and methods).

For example, given the following string:

```swift
let csv = "apple,pear,peach,orange,cherry,lime,goosberry"
```

One cannot write `csv[21]` to get the "g" in "orange", they must instead compute a value of type `String.Index` and supply that index instead. Note that these indices are not meant to be human-consumable on their own. They are what is referred to as _opaque indices_ ,as humans need not know what is inside them.
One cannot write `csv[21]` to get the "g" in "orange", they must instead compute a value of type `String.Index` and supply that index instead.
Note that these indices are not meant to be human-consumable on their own.
They are what is referred to as _opaque indices_ ,as humans need not know what is inside them.

```swift
let index = csv.index(csv.startIndex, offsetBy: 21)
csv[index]
// => "g"
// "g"
print(index)
// => Index(_rawBits: 1376513)
// prints Index(_rawBits: 1376513)
```

Note, however, that if the offset is not a valid index, i.e. if it would return the index before `startIndex` or after `endIndex` the operation will raise an error, crashing the program. To prevent this problem, one can specify a limiting index. This returns an optional index and it will return nil for otherwise invalid indices.
Note, however, that if the offset is not a valid index, i.e. if it would return the index before `startIndex` or after `endIndex` the operation will raise an error, crashing the program.
To prevent this problem, one can specify a limiting index.
This returns an optional index and it will return nil for otherwise invalid indices.

```swift
let tooFar = csv.index(csv.startIndex, offsetBy: 200)
// => error: Execution was interrupted, reason: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0).
// error: Execution was interrupted, reason: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0).
let tooFarButSafe = csv.index(csv.startIndex, offsetBy: 200, limitedBy: csv.endIndex)
// => nil
// nil
```

Additionally, indices cannot be shared between strings. For example, using the `index` of the "g" in "orange" computed above to index into the string `fam = "This is my family: 👨‍👩‍👦‍👦, this is our dog: 🐶, this is our cat: 🐱!"`, i.e. `fam[index]` will crash your program.
Additionally, indices cannot be shared between strings.
For example, using the `index` of the "g" in "orange" computed above to index into the string `fam = "This is my family: 👨‍👩‍👦‍👦, this is our dog: 🐶, this is our cat: 🐱!"`, i.e. `fam[index]` will crash your program.

There are many reasons for all of this, but they all basically boil down to "Unicode is tricky".

Generally speaking if you need random access to individual characters, you likely want to be using some other data structure, like `Array<Character>`.

## Some useful methods

Swift has the methods `first` and `last` to get the first and last character of a string.
These methods return an optional character, so if the string is empty, they will return nil.

```swift
let str = "Hello, world!"
print(str.first) // Prints Optional("H")
print(str.last) // Prints Optional("!")
```

You can also use the `prefix` and `suffix` methods to get a substring of a string.
These methods take an integer as an argument and return a substring containing the first or last n characters of the string.

```swift
let str = "Hello, world!"
print(str.prefix(5)) // Prints "Hello"
print(str.suffix(6)) // Prints "world!"
```

## Deep Dive: Unicode indexing

~~~~exercism/advanced

[Unicode][unicode] is a standard for encoding text, it features a large number of characters from many different languages and scripts.
It is a superset of ASCII, which means that all ASCII characters are also Unicode characters.
Unicode is a variable-length encoding, meaning that some characters take up more bytes than others.
And why that matters will be explained in the next section.

So we have to go other languages (than English) to actually see the problem, let's take the character "ü" from the German language (note that here are we using the 2 byte variant there is also a 1 byte version).
It may at first look like a single character, but it is actually two characters: "u" and "¨", so if you printed the length of the string "ü" it would be 2 in languages like Python.
However, as a user or a programmer we would say that it is a single character and Swift actually agrees with us.
But why does Swift do that but Python does not?
The answer is that Swift uses a different representation of strings than Python.
In Python, strings are represented as a sequence of bytes, which means that the length of a string is the number of bytes it takes up in memory.
In Swift, strings are represented as a sequence of characters, which means that the length of a string is the number of characters it contains.
This is a very important distinction, because it means that in Swift, the length of a string is not necessarily the same as the number of bytes it takes up in memory.
This in turn is what makes so we can't use integers to index into strings.
~~~~

[string-docs]: https://developer.apple.com/documentation/swift/String
[nsstring-docs]: https://developer.apple.com/documentation/foundation/nsstring
[string-format-specifiers]: https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Strings/Articles/formatSpecifiers.html
[string-interpolation]: https://docs.swift.org/swift-book/LanguageGuide/StringsAndCharacters.html#ID292
[string-indices]: https://docs.swift.org/swift-book/documentation/the-swift-programming-language/stringsandcharacters/#String-Indices
[unicode]: https://en.wikipedia.org/wiki/Unicode
62 changes: 58 additions & 4 deletions concepts/string-indexing/introduction.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,67 @@
# Introduction

Accessing individual characters in strings is both like and unlike accessing individual elements of an array. The first and last elements are easily accessed using the `first` and `last` properties of the string respectively. Note that, as a string may not have a first or last character, these properties return optional Characters which will need to be unwrapped if they are to be used.
Individual characters in a `String` can be accessed using the subscripting method used with arrays.
However, the [indices][string-indices] used for strings are _not_ integers and can not be worked with directly.
Instead they must be computed based off of some other known index, such as `startIndex`, which points to the position of the first character in a nonempty string, using the methods available in the [`String`][string-docs] and [`NSString`][nsstring-docs] libraries (when the `Foundation` module is imported, strings in Swift have access to all of the NSString properties and methods).

Other individual characters can be accessed using the subscripting method used with arrays. However, the indices used for strings are _not_ integers and can not be worked with directly. Instead they must be computed based off of some other known index, such as `startIndex`, which points to the position of the first character in a nonempty string.
For example, given the following string:

For example, you cannot write `csv[21]` to get the "g" in "orange", you must instead compute a value of type `String.Index`.
```swift
let csv = "apple,pear,peach,orange,cherry,lime,goosberry"
```

One cannot write `csv[21]` to get the "g" in "orange", they must instead compute a value of type `String.Index` and supply that index instead.
Note that these indices are not meant to be human-consumable on their own.
They are what is referred to as _opaque indices_ ,as humans need not know what is inside them.

```swift
let index = csv.index(csv.startIndex, offsetBy: 21)
csv[index]
// => "g"
// "g"
print(index)
// prints Index(_rawBits: 1376513)
```

Note, however, that if the offset is not a valid index, i.e. if it would return the index before `startIndex` or after `endIndex` the operation will raise an error, crashing the program.
To prevent this problem, one can specify a limiting index.
This returns an optional index and it will return nil for otherwise invalid indices.

```swift
let tooFar = csv.index(csv.startIndex, offsetBy: 200)
// error: Execution was interrupted, reason: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0).
let tooFarButSafe = csv.index(csv.startIndex, offsetBy: 200, limitedBy: csv.endIndex)
// nil
```

Additionally, indices cannot be shared between strings.
For example, using the `index` of the "g" in "orange" computed above to index into the string `fam = "This is my family: 👨‍👩‍👦‍👦, this is our dog: 🐶, this is our cat: 🐱!"`, i.e. `fam[index]` will crash your program.

There are many reasons for all of this, but they all basically boil down to "Unicode is tricky".

Generally speaking if you need random access to individual characters, you likely want to be using some other data structure, like `Array<Character>`.

## Some useful methods

Swift has the methods `first` and `last` to get the first and last character of a string.
These methods return an optional character, so if the string is empty, they will return nil.

```swift
let str = "Hello, world!"
print(str.first) // Prints Optional("H")
print(str.last) // Prints Optional("!")
```

You can also use the `prefix` and `suffix` methods to get a substring of a string.
These methods take an integer as an argument and return a substring containing the first or last n characters of the string.

```swift
let str = "Hello, world!"
print(str.prefix(5)) // Prints "Hello"
print(str.suffix(6)) // Prints "world!"
```

[string-docs]: https://developer.apple.com/documentation/swift/String
[nsstring-docs]: https://developer.apple.com/documentation/foundation/nsstring
[string-format-specifiers]: https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Strings/Articles/formatSpecifiers.html
[string-indices]: https://docs.swift.org/swift-book/documentation/the-swift-programming-language/stringsandcharacters/#String-Indices
[unicode]: https://en.wikipedia.org/wiki/Unicode
7 changes: 6 additions & 1 deletion concepts/string-indexing/links.json
Original file line number Diff line number Diff line change
@@ -1 +1,6 @@
[]
[
{
"url": "https://docs.swift.org/swift-book/documentation/the-swift-programming-language/stringsandcharacters/#String-Indices",
"description": "Swift Book: String Indices"
}
]
8 changes: 8 additions & 0 deletions concepts/string-methods/.meta/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"blurb": "Strings have a variety of methods that can be used to manipulate them.",
"authors": [
"wneumann",
"meatball133"
],
"contributors": []
}
Loading