Skip to content
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

Concept stringers with exercise #2208

Merged
merged 13 commits into from
May 29, 2022
Merged
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
9 changes: 9 additions & 0 deletions concepts/stringers/.meta/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"blurb": "Stringer is an interface for defining the string format of values.",
"authors": [
"norbs57"
],
"contributors": [
"andrerfcsantos"
]
}
84 changes: 84 additions & 0 deletions concepts/stringers/about.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
# About

[Stringer][stringer-interface] is an interface for defining the string format of values.

The interface consists of a single `String` method:

```go
type Stringer interface {
String() string
}
```

Types that want to implement this interface must have a `String()` method that returns a human-friendly string representation of the type. The [fmt][fmt-package] package (and many others) will look for this method to format and print values.

## Example: Distances

Assume we are working on an application that deals with geographical distances measured in different units.
We have defined types `DistanceUnit` and `Distance` as follows:

```go
type DistanceUnit int

const (
Kilometer DistanceUnit = 0
Mile DistanceUnit = 1
)

type Distance struct {
number float64
unit DistanceUnit
}
```

In the example above, `Kilometer` and `Mile` ane constants of type `DistanceUnit`.

These types do not implement interface `Stringer` as they lack the `String` method.
Hence `fmt` functions will print `Distance` values using Go's "default format":

```go
mileUnit := Mile
fmt.Sprint(mileUnit)
// => 1
// The result is '1' because that is the underlying value of the 'Mile' contant (see contant declarations above)

dist := Distance{number: 790.7, unit: Kilometer}
fmt.Sprint(dist)
// => {790.7 0}
// not a very useful output!
```

In order to make the output more informative, we implement interface `Stringer` for `DistanceUnit` and `Distance` types by adding a `String` method to each type:

```go
func (sc DistanceUnit) String() string {
units := []string{"km", "mi"}
return units[sc]
}

func (d Distance) String() string {
return fmt.Sprintf("%v %v", d.number, d.unit)
}
```

`fmt` package functions will call these methods when formatting `Distance` values:

```go
kmUnit := Kilometer
kmUnit.String()
// => km

mileUnit := Mile
mileUnit.String()
// => mi

dist := Distance{
number: 790.7,
unit: Kilometer,
}
dist.String()
// => 790.7 km
```

[stringer-interface]: https://pkg.go.dev/fmt#Stringer
[fmt-package]: https://pkg.go.dev/fmt
84 changes: 84 additions & 0 deletions concepts/stringers/introduction.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
# Introduction

[Stringer][stringer-interface] is an interface for defining the string format of values.

The interface consists of a single `String` method:

```go
type Stringer interface {
String() string
}
```

Types that want to implement this interface must have a `String()` method that returns a human-friendly string representation of the type. The [fmt][fmt-package] package (and many others) will look for this method to format and print values.

## Example: Distances

Assume we are working on an application that deals with geographical distances measured in different units.
We have defined types `DistanceUnit` and `Distance` as follows:

```go
type DistanceUnit int

const (
Kilometer DistanceUnit = 0
Mile DistanceUnit = 1
)

type Distance struct {
number float64
unit DistanceUnit
}
```

In the example above, `Kilometer` and `Mile` ane constants of type `DistanceUnit`.

These types do not implement interface `Stringer` as they lack the `String` method.
Hence `fmt` functions will print `Distance` values using Go's "default format":

```go
mileUnit := Mile
fmt.Sprint(mileUnit)
// => 1
// The result is '1' because that is the underlying value of the 'Mile' contant (see contant declarations above)

dist := Distance{number: 790.7, unit: Kilometer}
fmt.Sprint(dist)
// => {790.7 0}
// not a very useful output!
```

In order to make the output more informative, we implement interface `Stringer` for `DistanceUnit` and `Distance` types by adding a `String` method to each type:

```go
func (sc DistanceUnit) String() string {
units := []string{"km", "mi"}
return units[sc]
}

func (d Distance) String() string {
return fmt.Sprintf("%v %v", d.number, d.unit)
}
```

`fmt` package functions will call these methods when formatting `Distance` values:

```go
kmUnit := Kilometer
kmUnit.String()
// => km

mileUnit := Mile
mileUnit.String()
// => mi

dist := Distance{
number: 790.7,
unit: Kilometer,
}
dist.String()
// => 790.7 km
```

[stringer-interface]: https://pkg.go.dev/fmt#Stringer
[fmt-package]: https://pkg.go.dev/fmt
14 changes: 14 additions & 0 deletions concepts/stringers/links.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[
{
"url": "https://pkg.go.dev/fmt",
"description": "Go standard library: package fmt"
},
{
"url": "https://pkg.go.dev/fmt#Stringer",
"description": "Go standard library: interface Stringer"
},
{
"url": "https://go.dev/tour/methods/17",
"description": "A Tour of Go: Stringers"
}
]
21 changes: 21 additions & 0 deletions config.json
Original file line number Diff line number Diff line change
Expand Up @@ -429,6 +429,22 @@
"slices"
],
"status": "beta"
},
{
"slug": "meteorology",
"name": "Meteorology",
"uuid": "09fc7443-f5a2-4b19-8331-f3d5df3e5165",
"concepts": [
"stringers"
],
"prerequisites": [
"strings",
"string-formatting",
"interfaces",
"methods",
"structs"
],
"status": "beta"
}
],
"practice": [
Expand Down Expand Up @@ -2067,6 +2083,11 @@
"slug": "strings-package",
"uuid": "33708451-a03f-44ce-be05-eaacc1d5793c"
},
{
"name": "Stringers",
"slug": "stringers",
"uuid": "288c0d5b-173a-4446-b9a6-05f91fddd63e"
},
{
"name": "Structs",
"slug": "structs",
Expand Down
40 changes: 40 additions & 0 deletions exercises/concept/meteorology/.docs/hints.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Hints

## 1. Implement the `Stringer` interface for type `TemperatureUnit`

- The type `TemperatureUnit` is already created for you and represents a temperature unit
- Note that there are already 2 constants created for you that are of this type: `Celsius` and `Fahrenheit`, representing a temperature in Celsius and Fahrenheit, respectively
- Add a `String()` method to the `TemperatureUnit` type so it satisfies the `Stringer` interface. This method must return the string `"°C"` if the temperature unit is Celsius or `"°F"` if the temperature unit is Fahrenheit

## 2. Implement the `Stringer` interface for type `Temperature`

- Add a `String()` method to the `Temperature` type so it satisfies the `Stringer` interface
- The `String()` method should return a string with the numeric value for the temperature and the temperature unit separated by a space (`<temperature> <unit>`)
- The `Temperature` struct contains a `TemperatureUnit` and a `int`. You can use [fmt.Sprintf][sprintf] to help you format this string
- Since `TemperatureUnit` already implements the `Stringer` interface (from task 1), the functions of the `fmt` package like [fmt.Sprintf][sprintf] will know how to format it when you use the `%v` or `%s` [formatting verbs][fmt]

## 3. Implement the `Stringer` interface for type `SpeedUnit`

- The type `SpeedUnit` is already created for you and represents a temperature unit
- Note that there are already 2 constants created for you that are of this type: `KmPerHour` and `MilesPerHour`, representing a speed in kilometers per hour and miles per hour, respectively
- Add a `String()` method to the `SpeedUnit` type so it satisfies the `Stringer` interface. This method must return the string `"km/h"` if the speed unit is kilometers per hour or `"mph"` if the speed unit is miles per hour.

## 4. Implement the `Stringer` interface for `Speed`

- Add a `String()` method to the `Speed` type so it satisfies the `Stringer` interface
- The `String()` method should return a string with the numeric value for the speed and the speed unit separated by a space (`<speed> <unit>`)
- The `Speed` contains a `SpeedUnit` and a `int`. You can use [fmt.Sprintf][sprintf] to help you format this string
- Since `TemperatureUnit` struct already implements the `Stringer` interface (from task 3), the functions of the `fmt` package like [fmt.Sprintf][sprintf] will know how to format it when you use the `%v` or `%s` [formatting verbs][fmt]
- To insert a `%` in the final string when using [fmt.Sprintf][sprintf], use `%%` in the formatting string.

## 5. Implement the `Stringer` interface for type `MeteorologyData`

- The `Speed` contains a `Temperature` and a `Speed` fields, among other fields. You can use [fmt.Sprintf][sprintf] to help you format this string
- The `String` method should return the meteorology data in the following format: `"<location>: <temperature>, Wind <wind_speed> at <wind_direction>, <humidity>% Humidity"`
- Since the `Temperature` and `Speed` types already implement the `Stringer` interface (from task 2 and 4), the functions of the `fmt` package like [fmt.Sprintf][sprintf] will know how to format it when you use the `%v` or `%s` [formatting verbs][fmt]


[fmt]: https://pkg.go.dev/fmt
[sprint]: https://pkg.go.dev/fmt#Sprint
[sprintf]: https://pkg.go.dev/fmt#Sprintf
[yourbasic-enum]: https://yourbasic.org/golang/iota/#complete-enum-type-with-strings-best-practice
127 changes: 127 additions & 0 deletions exercises/concept/meteorology/.docs/instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
# Instructions

Your team is working on a meteorology application.
They have defined an API with various types and constants representing meteorological data, see file `meteorology.go`.

Your task is to add suitable `String` methods to all types so that they implement interface `Stringer`.
andrerfcsantos marked this conversation as resolved.
Show resolved Hide resolved

## 1. Implement the `Stringer` interface for type `TemperatureUnit`

After some discussion, the team have agreed that the unit of temperature will be either `Celsius` or `Fahrenheit`. Values should be formatted as shown in the examples below.

Make the `TemperatureUnit` type implement the `Stringer` interface by adding a `String` method to it. This method must return the string `"°C"` if the temperature unit is Celsius or `"°F"` if the temperature unit is Fahrenheit.

```go
andrerfcsantos marked this conversation as resolved.
Show resolved Hide resolved
temperatureUnit := Celsius
celsiusUnit := Celsius
fahrenheitUnit := Fahrenheit

celsiusUnit.String()
// => °C
fahrenheitUnit.String()
// => °F
fmt.Sprint(celsiusUnit)
// => °C
```

## 2. Implement the `Stringer` interface for type `Temperature`

Temperature values consist of an integer and a temperature unit. They should be formatted as in the examples below.

For that to happen, make the `Temperature` type implement the `Stringer` interface by adding a `String` method to it. This method should return a string with the numeric value for the temperature and the temperature unit separated by a space: `<temperature> <unit>`:


```go
celsiusTemp := Temperature{
degree: 21,
unit: Celsius,
}
celsiusTemp.String()
// => 21 °C
fmt.Sprint(celsiusTemp)
// => 21 °C

fahrenheitTemp := Temperature{
degree: 75,
unit: Fahrenheit,
}
fahrenheitTemp.String()
// => 75 °F
fmt.Sprint(fahrenheitTemp)
// => 75 °F
```

## 3. Implement the `Stringer` interface for type `SpeedUnit`

After lengthy discussions, the team has agreed that the unit of wind speed will be either `KmPerHour` or `MilesPerHour`. Values should be formatted as the examples below.

For that to happen, make the `SpeedUnit` type implement the `Stringer` interface by adding a `String` method to it. This method must return the string `"km/h"` if the speed unit is kilometers per hour or `"mph"` if the speed unit is miles per hour:


```go
mphUnit := MilesPerHour
mphUnit.String()
// => mph
fmt.Sprint(mphUnit)
// => mph

kmhUnit := KmPerHour
kmhUnit.String()
// => km/h
fmt.Sprint(mphUnit)
// => km/h
```

## 4. Implement the `Stringer` interface for `Speed`

Wind speed values consist of an integer and a speed unit. They should be formatted as in the example below.

For that to happen, make the `Speed` type implement the `Stringer` interface by adding a `String` method to it. This method should return a string with the numeric value for the speed and the speed unit separated by a space: `<speed> <unit>`:

```go
windSpeedNow := Speed{
magnitude: 18,
unit: KmPerHour,
}
windSpeedNow.String(windSpeedNow)
// => 18 km/h
fmt.Sprintf(windSpeedNow)
// => 18 km/h

windSpeedYesterday := Speed{
magnitude: 22,
unit: MilesPerHour,
}
windSpeedYesterday.String(windSpeedYesterday)
// => 22 mph
fmt.Sprint(windSpeedYesterday)
// => 22 mph
```

## 5. Implement the `Stringer` interface for type `MetData`

Meteorological data specifies location, temperature, wind direction, wind speed
and humidity. It should be formatted as in the example below:

For that to happen, make the `MeteorologyData` type implement the `Stringer` interface by adding a `String` method to it. This method should return the meteorology data in the following format: `"<location>: <temperature>, Wind <wind_speed> at <wind_direction>, <humidity>% Humidity"`:

```go
sfData := MeteorologyData{
location: "San Francisco",
temperature: Temperature{
degree: 57,
unit: Fahrenheit
},
windDirection: "NW",
windSpeed: Speed{
magnitude: 19,
unit: MilesPerHour
},
humidity: 60
}

sfData.String()
// => San Francisco: 57 °F, Wind NW at 19 mph, 60% Humidity
fmt.Sprint(sfData)
// => San Francisco: 57 °F, Wind NW at 19 mph, 60% Humidity
```
Loading