diff --git a/LICENSE-GHC.md b/LICENSE-GHC.md new file mode 100644 index 0000000..a2d787b --- /dev/null +++ b/LICENSE-GHC.md @@ -0,0 +1,32 @@ +The Glasgow Haskell Compiler License + +Copyright 2002, The University Court of the University of Glasgow. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +- Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. + +- Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +- Neither name of the University nor the names of its contributors may be +used to endorse or promote products derived from this software without +specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY COURT OF THE UNIVERSITY OF +GLASGOW AND THE CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +UNIVERSITY COURT OF THE UNIVERSITY OF GLASGOW OR THE CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + diff --git a/README.md b/README.md index 98a6f2c..c586520 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,12 @@ _Note_: This module is an improvement over `Data.Array` when working with immuta bower install purescript-lists ``` +## Licensing + +Some of this code is derived from GHC's standard libraries (`base`); +according to its terms, we have included GHC's license in the file +`LICENSE-GHC.md`. + ## Module documentation - [Data.List](docs/Data/List.md) diff --git a/docs/Data/List.md b/docs/Data/List.md index 8ef9063..74207b8 100644 --- a/docs/Data/List.md +++ b/docs/Data/List.md @@ -70,6 +70,10 @@ Running time: `O(n)` singleton :: forall a. a -> List a ``` +Create a list with a single element. + +Running time: `O(1)` + #### `(..)` ``` purescript @@ -133,6 +137,10 @@ termination. null :: forall a. List a -> Boolean ``` +Test whether a list is empty. + +Running time: `O(1)` + #### `length` ``` purescript @@ -193,6 +201,10 @@ Running time: `O(n)` head :: forall a. List a -> Maybe a ``` +Get the first element in a list, or `Nothing` if the list is empty. + +Running time: `O(1)`. + #### `last` ``` purescript @@ -240,6 +252,10 @@ Running time: `O(1)` index :: forall a. List a -> Int -> Maybe a ``` +Get the element at the specified index, or `Nothing` if the index is out-of-bounds. + +Running time: `O(n)` where `n` is the required index. + #### `(!!)` ``` purescript @@ -345,6 +361,10 @@ Running time: `O(n)` reverse :: forall a. List a -> List a ``` +Reverse a list. + +Running time: `O(n)` + #### `concat` ``` purescript @@ -417,6 +437,8 @@ a value. sort :: forall a. (Ord a) => List a -> List a ``` +Sort the elements of an list in increasing order. + #### `sortBy` ``` purescript @@ -432,6 +454,8 @@ compared using the specified ordering. slice :: forall a. Int -> Int -> List a -> List a ``` +Extract a sublist by a start and end index. + #### `take` ``` purescript @@ -536,6 +560,10 @@ Running time: `O(n)` nub :: forall a. (Eq a) => List a -> List a ``` +Remove duplicate elements from a list. + +Running time: `O(n^2)` + #### `nubBy` ``` purescript @@ -628,6 +656,19 @@ Running time: `O(n^2)` zipWith :: forall a b c. (a -> b -> c) -> List a -> List b -> List c ``` +Apply a function to pairs of elements at the same positions in two lists, +collecting the results in a new list. + +If one list is longer, elements will be discarded from the longer list. + +For example + +```purescript +zipWith (*) (1 : 2 : 3 : Nil) (4 : 5 : 6 : 7 Nil) == 4 : 10 : 18 : Nil +``` + +Running time: `O(min(m, n))` + #### `zipWithA` ``` purescript @@ -656,12 +697,31 @@ unzip :: forall a b. List (Tuple a b) -> Tuple (List a) (List b) Transforms a list of pairs into a list of first components and a list of second components. +#### `transpose` + +``` purescript +transpose :: forall a. List (List a) -> List (List a) +``` + +The 'transpose' function transposes the rows and columns of its argument. +For example, + + transpose ((1:2:3:Nil) : (4:5:6:Nil) : Nil) == + ((1:4:Nil) : (2:5:Nil) : (3:6:Nil) : Nil) + +If some of the rows are shorter than the following rows, their elements are skipped: + + transpose ((10:11:Nil) : (20:Nil) : Nil : (30:31:32:Nil) : Nil) == + ((10:20:30:Nil) : (11:31:Nil) : (32:Nil) : Nil) + #### `foldM` ``` purescript foldM :: forall m a b. (Monad m) => (a -> b -> m a) -> a -> List b -> m a ``` +Perform a fold using a monadic step function. + #### `toList` ``` purescript diff --git a/docs/Data/List/Lazy.md b/docs/Data/List/Lazy.md index 433835d..f382364 100644 --- a/docs/Data/List/Lazy.md +++ b/docs/Data/List/Lazy.md @@ -104,6 +104,10 @@ Running time: `O(1)` singleton :: forall a. a -> List a ``` +Create a list with a single element. + +Running time: `O(1)` + #### `(..)` ``` purescript @@ -152,6 +156,10 @@ Create a list by repeating another list null :: forall a. List a -> Boolean ``` +Test whether a list is empty. + +Running time: `O(1)` + #### `length` ``` purescript @@ -168,6 +176,10 @@ Running time: `O(n)` cons :: forall a. a -> List a -> List a ``` +Attach an element to the front of a lazy list. + +Running time: `O(1)` + #### `(:)` ``` purescript @@ -208,6 +220,10 @@ Running time: `O(n)` head :: forall a. List a -> Maybe a ``` +Get the first element in a list, or `Nothing` if the list is empty. + +Running time: `O(1)`. + #### `last` ``` purescript @@ -255,6 +271,10 @@ Running time: `O(1)` index :: forall a. List a -> Int -> Maybe a ``` +Get the element at the specified index, or `Nothing` if the index is out-of-bounds. + +Running time: `O(n)` where `n` is the required index. + #### `(!!)` ``` purescript @@ -343,6 +363,10 @@ Running time: `O(n)` reverse :: forall a. List a -> List a ``` +Reverse a list. + +Running time: `O(n)` + #### `concat` ``` purescript @@ -400,6 +424,10 @@ a value. take :: forall a. Int -> List a -> List a ``` +Take the specified number of elements from the front of a list. + +Running time: `O(n)` where `n` is the number of elements to take. + #### `takeWhile` ``` purescript @@ -482,6 +510,10 @@ Running time: `O(n)` nub :: forall a. (Eq a) => List a -> List a ``` +Remove duplicate elements from a list. + +Running time: `O(n^2)` + #### `nubBy` ``` purescript @@ -574,6 +606,19 @@ Running time: `O(n^2)` zipWith :: forall a b c. (a -> b -> c) -> List a -> List b -> List c ``` +Apply a function to pairs of elements at the same positions in two lists, +collecting the results in a new list. + +If one list is longer, elements will be discarded from the longer list. + +For example + +```purescript +zipWith (*) (1 : 2 : 3 : Nil) (4 : 5 : 6 : 7 Nil) == 4 : 10 : 18 : Nil +``` + +Running time: `O(min(m, n))` + #### `zip` ``` purescript @@ -584,6 +629,23 @@ Collect pairs of elements at the same positions in two lists. Running time: `O(min(m, n))` +#### `transpose` + +``` purescript +transpose :: forall a. List (List a) -> List (List a) +``` + +The 'transpose' function transposes the rows and columns of its argument. +For example, + + transpose ((1:2:3:nil) : (4:5:6:nil) : nil) == + ((1:4:nil) : (2:5:nil) : (3:6:nil) : nil) + +If some of the rows are shorter than the following rows, their elements are skipped: + + transpose ((10:11:nil) : (20:nil) : nil : (30:31:32:nil) : nil) == + ((10:20:30:nil) : (11:31:nil) : (32:nil) : nil) + #### `toList` ``` purescript diff --git a/src/Data/List.purs b/src/Data/List.purs index b6a8db7..613cec7 100644 --- a/src/Data/List.purs +++ b/src/Data/List.purs @@ -80,6 +80,8 @@ module Data.List , zip , unzip + , transpose + , foldM , toList @@ -674,6 +676,26 @@ zip = zipWith Tuple unzip :: forall a b. List (Tuple a b) -> Tuple (List a) (List b) unzip = foldr (\(Tuple a b) (Tuple as bs) -> Tuple (Cons a as) (Cons b bs)) (Tuple Nil Nil) +-------------------------------------------------------------------------------- +-- Transpose ------------------------------------------------------------------- +-------------------------------------------------------------------------------- + +-- | The 'transpose' function transposes the rows and columns of its argument. +-- | For example, +-- | +-- | transpose ((1:2:3:Nil) : (4:5:6:Nil) : Nil) == +-- | ((1:4:Nil) : (2:5:Nil) : (3:6:Nil) : Nil) +-- | +-- | If some of the rows are shorter than the following rows, their elements are skipped: +-- | +-- | transpose ((10:11:Nil) : (20:Nil) : Nil : (30:31:32:Nil) : Nil) == +-- | ((10:20:30:Nil) : (11:31:Nil) : (32:Nil) : Nil) +transpose :: forall a. List (List a) -> List (List a) +transpose Nil = Nil +transpose (Cons Nil xss) = transpose xss +transpose (Cons (Cons x xs) xss) = + (x : mapMaybe head xss) : transpose (xs : mapMaybe tail xss) + -------------------------------------------------------------------------------- -- Folding --------------------------------------------------------------------- -------------------------------------------------------------------------------- @@ -683,6 +705,10 @@ foldM :: forall m a b. (Monad m) => (a -> b -> m a) -> a -> List b -> m a foldM _ a Nil = return a foldM f a (Cons b bs) = f a b >>= \a' -> foldM f a' bs +-------------------------------------------------------------------------------- +-- Deprecated functions -------------------------------------------------------- +-------------------------------------------------------------------------------- + -- | *Deprecated.* Use `fromFoldable` instead. `toList` will be removed in a -- | later version. toList :: forall f a. (Foldable f) => f a -> List a diff --git a/src/Data/List/Lazy.purs b/src/Data/List/Lazy.purs index 4986ddb..6fbc1b6 100644 --- a/src/Data/List/Lazy.purs +++ b/src/Data/List/Lazy.purs @@ -87,6 +87,8 @@ module Data.List.Lazy , zip -- , unzip + , transpose + -- , foldM , toList , fromList @@ -623,6 +625,36 @@ zipWith f xs ys = List (go <$> runList xs <*> runList ys) zip :: forall a b. List a -> List b -> List (Tuple a b) zip = zipWith Tuple +-------------------------------------------------------------------------------- +-- Transpose ------------------------------------------------------------------- +-------------------------------------------------------------------------------- + +-- | The 'transpose' function transposes the rows and columns of its argument. +-- | For example, +-- | +-- | transpose ((1:2:3:nil) : (4:5:6:nil) : nil) == +-- | ((1:4:nil) : (2:5:nil) : (3:6:nil) : nil) +-- | +-- | If some of the rows are shorter than the following rows, their elements are skipped: +-- | +-- | transpose ((10:11:nil) : (20:nil) : nil : (30:31:32:nil) : nil) == +-- | ((10:20:30:nil) : (11:31:nil) : (32:nil) : nil) +transpose :: forall a. List (List a) -> List (List a) +transpose xs = + case uncons xs of + Nothing -> + xs + Just { head: h, tail: xss } -> + case uncons h of + Nothing -> + transpose xss + Just { head: x, tail: xs } -> + (x : mapMaybe head xss) : transpose (xs : mapMaybe tail xss) + +-------------------------------------------------------------------------------- +-- Deprecated functions -------------------------------------------------------- +-------------------------------------------------------------------------------- + -- | *Deprecated.* Use `fromFoldable` instead. `toList` will be removed in a -- | later version. toList :: forall f a. (Foldable f) => f a -> List a diff --git a/test/Test/Data/List.purs b/test/Test/Data/List.purs index 5e7dadf..839d74b 100644 --- a/test/Test/Data/List.purs +++ b/test/Test/Data/List.purs @@ -306,6 +306,17 @@ testList = do -- primes = eratos $ upFrom 2 -- assert $ L.fromList (L.take 10 primes) == [2, 3, 5, 7, 11, 13, 17, 19, 23, 29] + log "transpose" + assert $ transpose (l [l [1,2,3], l[4,5,6], l [7,8,9]]) == + (l [l [1,4,7], l[2,5,8], l [3,6,9]]) + log "transpose skips elements when rows don't match" + assert $ transpose ((10:11:Nil) : (20:Nil) : Nil : (30:31:32:Nil) : Nil) == + ((10:20:30:Nil) : (11:31:Nil) : (32:Nil) : Nil) + log "transpose Nil == Nil" + assert $ transpose Nil == (Nil :: List (List Int)) + log "transpose (singleton Nil) == Nil" + assert $ transpose (singleton Nil) == (Nil :: List (List Int)) + step :: Int -> Maybe (Tuple Int Int) step 6 = Nothing step n = Just (Tuple n (n + 1)) diff --git a/test/Test/Data/List/Lazy.purs b/test/Test/Data/List/Lazy.purs index 2589b48..70c7a2c 100644 --- a/test/Test/Data/List/Lazy.purs +++ b/test/Test/Data/List/Lazy.purs @@ -270,6 +270,19 @@ testListLazy = do -- primes = eratos $ upFrom 2 -- assert $ L.fromList (L.take 10 primes) == [2, 3, 5, 7, 11, 13, 17, 19, 23, 29] + -- + -- + -- + log "transpose" + assert $ transpose (l [l [1,2,3], l[4,5,6], l [7,8,9]]) == + (l [l [1,4,7], l[2,5,8], l [3,6,9]]) + log "transpose skips elements when rows don't match" + assert $ transpose ((10:11:nil) : (20:nil) : nil : (30:31:32:nil) : nil) == + ((10:20:30:nil) : (11:31:nil) : (32:nil) : nil) + log "transpose nil == nil" + assert $ transpose nil == (nil :: List (List Int)) + log "transpose (singleton nil) == nil" + assert $ transpose (singleton nil) == (nil :: List (List Int)) nil' :: List Int nil' = nil diff --git a/test/Test/Data/List/ZipList.purs b/test/Test/Data/List/ZipList.purs new file mode 100644 index 0000000..4d5d03a --- /dev/null +++ b/test/Test/Data/List/ZipList.purs @@ -0,0 +1,29 @@ +module Test.Data.List.ZipList (testZipList) where + +import Prelude +import Control.Monad.Eff (Eff()) +import Control.Monad.Eff.Console (CONSOLE(), log) +import Data.Foldable +import Data.Monoid.Additive +import Data.List.Lazy as LazyList +import Data.List.ZipList (ZipList(..)) +import Data.Maybe (Maybe(..), isNothing) +import Data.Maybe.Unsafe (fromJust) +import Data.Tuple (Tuple(..)) +import Data.Array as Array +import Test.Assert (ASSERT(), assert) + +testZipList :: forall eff. Eff (assert :: ASSERT, console :: CONSOLE | eff) Unit +testZipList = do + log "ZipList Applicative instance should be zippy" + testZipWith (+) [1,2,3] [4,5,6] + testZipWith (*) [1,2,3] [4,5,6] + testZipWith const [1,2,3] [4,5,6] + +testZipWith :: forall a b c eff. (Eq c) => (a -> b -> c) -> Array a -> Array b -> Eff (assert :: ASSERT, console :: CONSOLE | eff) Unit +testZipWith f xs ys = + assert $ (f <$> l xs <*> l ys) == l (Array.zipWith f xs ys) + +-- Shortcut for constructing a ZipList +l :: forall a. Array a -> ZipList a +l = ZipList <<< LazyList.fromFoldable diff --git a/test/Test/Main.purs b/test/Test/Main.purs index 3ce5887..30f4284 100644 --- a/test/Test/Main.purs +++ b/test/Test/Main.purs @@ -6,10 +6,12 @@ import Control.Monad.Eff.Console (CONSOLE()) import Test.Assert (ASSERT()) import Test.Data.List import Test.Data.List.Lazy +import Test.Data.List.ZipList import Test.Data.List.Unsafe main :: forall eff. Eff (assert :: ASSERT, console :: CONSOLE | eff) Unit main = do testList testListLazy + testZipList testListUnsafe