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

Implementing exercise binary-search #615

Closed
wants to merge 7 commits into from
Closed
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
10 changes: 10 additions & 0 deletions config.json
Original file line number Diff line number Diff line change
Expand Up @@ -462,6 +462,16 @@
"Number Theory"
]
},
{
"uuid": "e714ed04-e633-4814-8885-323968f39046",
"slug": "binary-search",
"core": false,
"unlocked_by": null,
"difficulty": 5,
"topics": [
"Maybe"
]
},
{
"uuid": "4b6d04f4-8cfc-4d8a-ac6e-77f3889f27c3",
"slug": "atbash-cipher",
Expand Down
79 changes: 79 additions & 0 deletions exercises/binary-search/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# Binary Search

Implement a binary search algorithm.

Searching a sorted collection is a common task. A dictionary is a sorted list of word definitions. Given a word, one can find its definition. A telephone book is a sorted list of people's names, addresses, and telephone numbers. Knowing someone's name allows one to quickly find their telephone number and address.

If the list to be searched contains more than a few items (a dozen, say) a binary search will require far fewer comparisons than a linear search, but it imposes the requirement that the list be sorted.

In computer science, a binary search or half-interval search algorithm finds the position of a specified input value (the search "key") within an array sorted by key value.

In each step, the algorithm compares the search key value with the key value of the middle element of the array.

If the keys match, then a matching element has been found and its index, is returned.

Otherwise, if the search key is less than the middle element's key, then the algorithm repeats its action on the sub-array to the left of the middle element or, if the search key is greater, on the sub-array to the right.

If the remaining array to be searched is empty, then the key cannot be found in the array and Nothing is returned.

A binary search halves the number of items to check with each iteration, so locating an item (or determining its absence) takes logarithmic time. A binary search is a dichotomic divide and conquer search algorithm.


## Getting Started

For installation and learning resources, refer to the
[exercism help page](http://exercism.io/languages/haskell).

## Running the tests

To run the test suite, execute the following command:

```bash
stack test
```

#### If you get an error message like this...

```
No .cabal file found in directory
```

You are probably running an old stack version and need
to upgrade it.

#### Otherwise, if you get an error message like this...

```
No compiler found, expected minor version match with...
Try running "stack setup" to install the correct GHC...
```

Just do as it says and it will download and install
the correct compiler version:

```bash
stack setup
```

## Running *GHCi*

If you want to play with your solution in GHCi, just run the command:

```bash
stack ghci
```

## Feedback, Issues, Pull Requests

The [exercism/haskell](https://github.com/exercism/haskell) repository on
GitHub is the home for all of the Haskell exercises.

If you have feedback about an exercise, or want to help implementing a new
one, head over there and create an issue. We'll do our best to help you!

## Source

Wikipedia [https://en.wikipedia.org/wiki/Isogram](https://en.wikipedia.org/wiki/Isogram)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why isogram?


## Submitting Incomplete Solutions
It's possible to submit an incomplete solution so you can see how others have completed the exercise.
17 changes: 17 additions & 0 deletions exercises/binary-search/examples/success-standard/package.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
name: binary-search

dependencies:
- base
- array

library:
exposed-modules: BinarySearch
source-dirs: src

tests:
test:
main: Tests.hs
source-dirs: test
dependencies:
- binary-search
- hspec
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
module BinarySearch (binarySearch) where
import Data.Array (Array, (!))

binFind :: Array Int Int -> Int -> Int -> Int -> Int
binFind arr x lwr upr
| middleVal == x = midInd
| x > middleVal = binFind arr x (midInd + 1) upr
| otherwise = binFind arr x lwr (midInd - 1)
where midInd = abs (upr + lwr) `div` 2
middleVal = arr ! midInd

binarySearch :: Array Int Int -> Int -> Maybe Int
binarySearch arr x
| null arr = Nothing
| length arr == 1 = if arr ! 0 == x then Just 0 else Nothing
| x < (arr ! 0) || x > (arr ! (length arr - 1)) = Nothing
| otherwise = Just $ binFind arr x 0 $ length arr
21 changes: 21 additions & 0 deletions exercises/binary-search/package.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
name: binary-search
version: 1.0.0

dependencies:
- array
- base

library:
exposed-modules: BinarySearch
source-dirs: src
dependencies:
# - foo # List here the packages you
# - bar # want to use in your solution.

tests:
test:
main: Tests.hs
source-dirs: test
dependencies:
- binary-search
- hspec
5 changes: 5 additions & 0 deletions exercises/binary-search/src/BinarySearch.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module BinarySearch (binarySearch) where
import Data.Array (Array)

binarySearch :: Array Int Int -> Int -> Maybe Int
binarySearch = error "You need to implement this function!"
1 change: 1 addition & 0 deletions exercises/binary-search/stack.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
resolver: lts-9.11
74 changes: 74 additions & 0 deletions exercises/binary-search/test/Tests.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
{-# LANGUAGE RecordWildCards #-}

import Data.Foldable (for_)
import Test.Hspec (Spec, describe, it, shouldBe)
import Test.Hspec.Runner (configFastFail, defaultConfig, hspecWith)
import Data.Array (Array, listArray)

import BinarySearch (binarySearch)

main :: IO ()
main = hspecWith defaultConfig {configFastFail = True} specs

specs :: Spec
specs = describe "foundElement" $ for_ cases test
where
test Case{..} = it description assertion
where
assertion = binarySearch input value `shouldBe` expected


data Case a = Case { description :: String
, input :: Array Int a
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would indent this line two spaces further so that the comma lines up with the open bracket on the previous row.

, value :: a
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would indent this line two spaces further so that the comma lines up with the open bracket on the previous row.

same

, expected :: Maybe Int
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would indent this line two spaces further so that the comma lines up with the open bracket on the previous row.

same

}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would indent this line two spaces further so that the comma lines up with the open bracket on the previous row.

same except now I'm talking about the closing bracket


cases :: [Case Int]
cases = [ Case { description = "finds a value in an array with one element"
, input = listArray (0, 0) [6]
, value = 6
, expected = Just 0
}
, Case { description = "finds a value in the middle of an array"
, input = array1
, value = 6
, expected = Just 3
}
, Case { description = "finds a value at the beginning of an array"
, input = array1
, value = 1
, expected = Just 0
}
, Case { description = "finds a value at the end of an array"
, input = array1
, value = 11
, expected = Just 6
}
, Case { description = "finds a value in an array of odd length"
, input = listArray (0, 12) [1, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 634]
, value = 144
, expected = Just 9
}
, Case { description = "finds a value in an array of even length"
, input = listArray (0, 11) [1, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377]
, value = 21
, expected = Just 5
}
, Case { description = "a value smaller than the array's smallest value is not included"
, input = array1
, value = 0
, expected = Nothing
}
, Case { description = "a value larger than the array's largest value is not included"
, input = array1
, value = 13
, expected = Nothing
}
, Case { description = "nothing is included in an empty array"
, input = listArray (1, 0) []
, value = 1
, expected = Nothing
}
]
where array1 = listArray (0, 6) [1, 3, 4, 6, 8, 9, 11]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm considering whether this should go above the cases so that I can see it before I see the cases it's going to be used in. I'm not sure, so let me know what you think about it.