Skip to content

Commit

Permalink
Add list concept
Browse files Browse the repository at this point in the history
  • Loading branch information
meatball133 committed Sep 30, 2024
1 parent 594515b commit 8f8817f
Show file tree
Hide file tree
Showing 16 changed files with 550 additions and 0 deletions.
6 changes: 6 additions & 0 deletions concepts/lists/.meta/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"blurb": "Lists are a basic data type in Haskell for holding a collection of values. A list can hold values of different types. Lists are immutable, meaning they cannot be modified. Lists in Haskell are implemented as linked lists. Therefore, accessing an element in a list takes linear time depending on the length of the list.",
"authors": [
"meatball133"
]
}
87 changes: 87 additions & 0 deletions concepts/lists/about.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# About

[Lists][list] are a basic data type in Haskell for holding a collection of values.
Lists are _immutable_, meaning they cannot be modified.
Any operation that changes a list returns a new list.
There are several methods in the prelude which allows you to work with Lists.

Lists in Haskell are implemented as [linked lists][linked-list-wiki], and not as arrays of contiguous memory location.
Therefore, accessing an element in a list takes linear time depending on the length of the list.

Lists can be written in literal form, head-tail notation, (which uses the `cons` operator `:`), or a combination of both:

```haskell
-- Literal Form
[]
[1]
[1, 2, 3]

-- Head-tail Notation
[]
-- same as [1]
1 : []
-- same as [1, 2, 3]
1 : (2 : (3 : []))

-- Mixed
-- same as [1, 2, 3]
1 : [2, 3]
```

Head-tail notation can be used to append items to a list.

```haskell
list = [2, 1]

[3, 2, 1] == 3 : list
-- -> True
```

Appending elements to a list during iteration is considered an anti-pattern.
Appending an element requires walking through the entire list and adding the element at the end, therefore, appending a new element in each iteration would require walking through the entire list in each iteration.

We can achieve the same result by prepending an element to the reversed list, and then reversing the result. Prepending is a fast operation and requires constant time.

```haskell
-- Appending to the end of a list (potentially slow)
[1, 2, 3] ++ [4] ++ [5] ++ [6]

-- Prepend to the start of a list (faster, due to the nature of linked lists)
6 : (5 : (4 : [3, 2, 1]))
-- then reverse!
```

There are several common Prelude functions for lists:

- [`head`][head] returns the _head_ of a list -- the _first_ item in a list.
- [`tail`][tail] returns the _tail_ of the list -- the list _minus_ the _first_ item.
- [`length`][length] returns the number items in the list.
- [`elem`][in] returns a boolean value indicating whether the item is an element in the list.

There is also the [`List` module][list].

Lists can only contain one data type.

```haskell
list = [1, "string"]
-- Error: No instance for (Num String) arising from the literal ‘1’
```

## Type annotation

The type annotation of a list is `[a]` where a is the type which the lists holds, for example `String` or `Int`.

``` haskell
a :: [Int]
a = [1, 2, 3]
```

[enum]: https://hexdocs.pm/elixir/Enum.html
[enum-protocol]: https://hexdocs.pm/elixir/Enumerable.html
[hd]: https://hexdocs.pm/elixir/Kernel.html#hd/1
[in]: https://hexdocs.pm/elixir/Kernel.html#in/2
[length]: https://hexdocs.pm/elixir/Kernel.html#length/1
[list]: https://hexdocs.pm/elixir/List.html
[stream]: https://hexdocs.pm/elixir/Stream.html
[tl]: https://hexdocs.pm/elixir/Kernel.html#tl/1
[linked-list-wiki]: https://en.wikipedia.org/wiki/Linked_list
87 changes: 87 additions & 0 deletions concepts/lists/introduction.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# About

[Lists][list] are a basic data type in Haskell for holding a collection of values.
Lists are _immutable_, meaning they cannot be modified.
Any operation that changes a list returns a new list.
There are several methods in the prelude which allows you to work with Lists.

Lists in Haskell are implemented as [linked lists][linked-list-wiki], and not as arrays of contiguous memory location.
Therefore, accessing an element in a list takes linear time depending on the length of the list.

Lists can be written in literal form, head-tail notation, (which uses the `cons` operator `:`), or a combination of both:

```haskell
-- Literal Form
[]
[1]
[1, 2, 3]

-- Head-tail Notation
[]
-- same as [1]
1 : []
-- same as [1, 2, 3]
1 : (2 : (3 : []))

-- Mixed
-- same as [1, 2, 3]
1 : [2, 3]
```

Head-tail notation can be used to append items to a list.

```haskell
list = [2, 1]

[3, 2, 1] == 3 : list
-- true
```

Appending elements to a list during iteration is considered an anti-pattern.
Appending an element requires walking through the entire list and adding the element at the end, therefore, appending a new element in each iteration would require walking through the entire list in each iteration.

We can achieve the same result by prepending an element to the reversed list, and then reversing the result. Prepending is a fast operation and requires constant time.

```haskell
-- Appending to the end of a list (potentially slow)
[1, 2, 3] ++ [4] ++ [5] ++ [6]

-- Prepend to the start of a list (faster, due to the nature of linked lists)
6 : (5 : (4 : [3, 2, 1]))
-- then reverse!
```

There are several common Prelude functions for lists:

- [`head`][head] returns the _head_ of a list -- the _first_ item in a list.
- [`tail`][tail] returns the _tail_ of the list -- the list _minus_ the _first_ item.
- [`length`][length] returns the number items in the list.
- [`elem`][in] returns a boolean value indicating whether the item is an element in the list.

There is also the [`List` module][list].

Lists can only contain one data type.

```haskell
list = [1, "string"]
-- Error: No instance for (Num String) arising from the literal ‘1’
```

## Type annotation

The type annotation of a list is `[a]` where a is the type which the lists holds, for example `String` or `Int`.

``` haskell
a :: [Int]
a = [1, 2, 3]
```

[enum]: https://hexdocs.pm/elixir/Enum.html
[enum-protocol]: https://hexdocs.pm/elixir/Enumerable.html
[hd]: https://hexdocs.pm/elixir/Kernel.html#hd/1
[in]: https://hexdocs.pm/elixir/Kernel.html#in/2
[length]: https://hexdocs.pm/elixir/Kernel.html#length/1
[list]: https://hexdocs.pm/elixir/List.html
[stream]: https://hexdocs.pm/elixir/Stream.html
[tl]: https://hexdocs.pm/elixir/Kernel.html#tl/1
[linked-list-wiki]: https://en.wikipedia.org/wiki/Linked_list
1 change: 1 addition & 0 deletions concepts/lists/links.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[]
8 changes: 8 additions & 0 deletions exercises/concept/language-list/.docs/hints.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# General

- Basic numbers operators are described in the Haskell [GHC.Num module documentation](https://hackage.haskell.org/package/base-4.16.0.0/docs/GHC-Num.html). But you might prefer a more easily digestable [basic introduction.](https://www.tutorialspoint.com/haskell/haskell_basic_operators.htm)

# Modules and Indentation

- [Declaring modules](https://learnyouahaskell.github.io/modules#making-our-own-modules)
- [Indentation rules](https://en.wikibooks.org/wiki/Haskell/Indentation)
66 changes: 66 additions & 0 deletions exercises/concept/language-list/.docs/instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# Instructions

In this exercise you need to implement some functions to manipulate a list of programming languages.

## 1. Define a function to return an empty language list

Define the `new` function that takes no arguments and returns an empty list.

```haskell
new
-- -> []
```

## 2. Define a function to add a language to the list

Define the `add/2` function that takes 2 arguments (a _language list_ and a string literal of a _language_).
It should return the resulting list with the new language prepended to the given list.

```haskell
add new "Clojure"
-- -> ["Clojure"]
add ["Clojure"] "Haskell"
-- -> ["Haskell", "Clojure"]
```

## 3. Define a function to remove a language from the list

Define the `remove` function that takes 1 argument (a _language list_).
It should return the list without the first item. Assume the list will always have at least one item.

```haskell
remove ["Haskell", "Clojure", "Erlang"]
-- -> ["Clojure", "Erlang"]
```

## 4. Define a function to return the first item in the list

Define the `first` function that takes 1 argument (a _language list_).
It should return the first language in the list.
Assume the list will always have at least one item.

```haskell
first ["Elixir", "Haskell", "Clojure", "Prolog"]
-- -> "Elixir"
```

## 5. Define a function to return how many languages are in the list

Define the `count` function that takes 1 argument (a _language list_).
It should return the number of languages in the list.

```haskell
count ["Prolog", "Elm"]
-- -> 2
```

## 6. Define a function to determine if the list includes a functional language

Define the `isFunctionalList` function which takes 1 argument (a _language list_).
It should return a boolean value.
It should return `True` if _"Haskell"_ is one of the languages in the list.

```haskell
isFunctionalList ["Haskell"]
-- -> True
```
87 changes: 87 additions & 0 deletions exercises/concept/language-list/.docs/introduction.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# About

[Lists][list] are a basic data type in Haskell for holding a collection of values.
Lists are _immutable_, meaning they cannot be modified.
Any operation that changes a list returns a new list.
There are several methods in the prelude which allows you to work with Lists.

Lists in Haskell are implemented as [linked lists][linked-list-wiki], and not as arrays of contiguous memory location.
Therefore, accessing an element in a list takes linear time depending on the length of the list.

Lists can be written in literal form, head-tail notation, (which uses the `cons` operator `:`), or a combination of both:

```haskell
-- Literal Form
[]
[1]
[1, 2, 3]

-- Head-tail Notation
[]
-- same as [1]
1 : []
-- same as [1, 2, 3]
1 : (2 : (3 : []))

-- Mixed
-- same as [1, 2, 3]
1 : [2, 3]
```

Head-tail notation can be used to append items to a list.

```haskell
list = [2, 1]

[3, 2, 1] == 3 : list
-- -> True
```

Appending elements to a list during iteration is considered an anti-pattern.
Appending an element requires walking through the entire list and adding the element at the end, therefore, appending a new element in each iteration would require walking through the entire list in each iteration.

We can achieve the same result by prepending an element to the reversed list, and then reversing the result. Prepending is a fast operation and requires constant time.

```haskell
-- Appending to the end of a list (potentially slow)
[1, 2, 3] ++ [4] ++ [5] ++ [6]

-- Prepend to the start of a list (faster, due to the nature of linked lists)
6 : (5 : (4 : [3, 2, 1]))
-- then reverse!
```

There are several common Prelude functions for lists:

- [`head`][head] returns the _head_ of a list -- the _first_ item in a list.
- [`tail`][tail] returns the _tail_ of the list -- the list _minus_ the _first_ item.
- [`length`][length] returns the number items in the list.
- [`elem`][in] returns a boolean value indicating whether the item is an element in the list.

There is also the [`List` module][list].

Lists can only contain one data type.

```haskell
list = [1, "string"]
-- Error: No instance for (Num String) arising from the literal ‘1’
```

## Type annotation

The type annotation of a list is `[a]` where a is the type which the lists holds, for example `String` or `Int`.

``` haskell
a :: [Int]
a = [1, 2, 3]
```

[enum]: https://hexdocs.pm/elixir/Enum.html
[enum-protocol]: https://hexdocs.pm/elixir/Enumerable.html
[hd]: https://hexdocs.pm/elixir/Kernel.html#hd/1
[in]: https://hexdocs.pm/elixir/Kernel.html#in/2
[length]: https://hexdocs.pm/elixir/Kernel.html#length/1
[list]: https://hexdocs.pm/elixir/List.html
[stream]: https://hexdocs.pm/elixir/Stream.html
[tl]: https://hexdocs.pm/elixir/Kernel.html#tl/1
[linked-list-wiki]: https://en.wikipedia.org/wiki/Linked_list
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Teaches the basics concept, wherein we want the students to learn how to define functions,
and we found that people usually learn best when they have to write things from scratch.
https://github.com/exercism/haskell/pull/1026#issuecomment-988556486
25 changes: 25 additions & 0 deletions exercises/concept/language-list/.meta/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"authors": [
"meatball133"
],
"files": {
"solution": [
"src/LanguageList.hs",
"package.yaml"
],
"test": [
"test/Tests.hs"
],
"exemplar": [
".meta/exemplar/src/LanguageListLanguageList.hs"
],
"invalidator": [
"stack.yaml"
]
},
"forked_from": [
"elixir/language-list"
],
"icon": "language-list",
"blurb": "Learn about lists by keeping track of the programming languages you're currently learning on Exercism."
}
Loading

0 comments on commit 8f8817f

Please sign in to comment.