diff --git a/README.md b/README.md index 5ae7b21..1330039 100644 --- a/README.md +++ b/README.md @@ -4,4 +4,54 @@ [![MPL-2.0 license](https://img.shields.io/badge/license-MPL--2.0-blue.svg)](LICENSE) [![Build status](https://secure.travis-ci.org/vrom911/slist.svg)](https://travis-ci.org/vrom911/slist) -Sized list \ No newline at end of file +This package introduces sized list data type — `Slist`. The data type +has the following shape: + +```haskell +data Slist a = Slist + { sList :: [a] + , sSize :: Size + } +``` + +As you can see along with the familiar list, it contains `Size` field that +represents the size of the structure. Slists can be finite or infinite, and this +is expressed with `Size`. + +```haskell +data Size + = Size Int + | Infinity +``` + +This representation of the list gives some additional advantages. Getting the +length of the list is the "free" operation (runs in `O(1)`). This property +helps to improve the performance for a bunch of functions like `take`, `drop`, +`at`, etc. But also it doesn't actually add any overhead on the existing +functions. + +Also, this allows to write a number of safe functions like `safeReverse`, +`safeHead`, `safeLast`, `safeIsSuffixOf`, etc. + +## Comparison + +Check out the comparison table between lists and slists performance. + +| Function | list (finite) | list (infinite) | Slist (finite) | Slist (infinite) | +|-------------------|-----------------------------------|-----------------------------|----------------------------------------|------------------| +| `length` | `O(n)` | <_hangs_> | `O(1)` | `O(1)` | +| `safeLast` | `O(n)` | <_hangs_> | `O(n)` | `O(1)` | +| `init` | `O(n)` | <_hangs_> | `O(n)` | `O(1)` | +| `take` | `O(min i n)` | `O(i)` | `0 < i < n`: `O(i)`; otherwise: `O(1)` | `O(i)` | +| `at` | `O(min i n)` (run-time exception) | `O(i)` (run-time exception) | `0 < i < n`: `O(i)`; otherwise: `O(1)` | `O(i)` | +| `safeStripPrefix` | `O(m)` | `O(m)` (can hang) | `O(m)` | `O(m)` | + +## Potential usage cases + +* When you ask the length of the list too frequently. +* When you need to convert to data structures that require to know the list + size in advance for allocating an array of the elements. + _Example:_ [Vector data structure](https://hackage.haskell.org/package/vector). +* When you need to serialised lists. +* When you need to control the behaviour depending on the finiteness of the list. +* When you need a more efficient or safe implementation of some functions. diff --git a/slist.cabal b/slist.cabal index a9c47f8..80b5846 100644 --- a/slist.cabal +++ b/slist.cabal @@ -2,7 +2,8 @@ cabal-version: 2.0 name: slist version: 0.0.0 synopsis: Sized list -description: Sized list +description: This package implements @Slist@ data structure that stores the size + of the list along with the list itself. homepage: https://github.com/vrom911/slist bug-reports: https://github.com/vrom911/slist/issues license: MPL-2.0 @@ -14,7 +15,9 @@ category: Data Structures, List build-type: Simple extra-doc-files: README.md , CHANGELOG.md -tested-with: GHC == 8.2.2, GHC == 8.4.4, GHC == 8.6.3 +tested-with: GHC == 8.2.2 + , GHC == 8.4.4 + , GHC == 8.6.3 source-repository head type: git @@ -25,10 +28,8 @@ library exposed-modules: Slist Slist.Size - build-depends: base >= 4.10.1.0 && < 4.13 - ghc-options: -Wall -Wincomplete-uni-patterns -Wincomplete-record-updates @@ -60,7 +61,6 @@ test-suite slist-test build-depends: base >= 4.10.1.0 && < 4.13 , slist - ghc-options: -Wall -threaded -rtsopts @@ -95,7 +95,6 @@ test-suite slist-doctest build-depends: base >= 4.9 && < 5 , doctest , Glob - , QuickCheck ghc-options: -threaded default-language: Haskell2010 \ No newline at end of file diff --git a/src/Slist.hs b/src/Slist.hs index 0bcf4e3..d3ec71f 100644 --- a/src/Slist.hs +++ b/src/Slist.hs @@ -7,7 +7,68 @@ Copyright: (c) 2019 vrom911 License: MPL-2.0 Maintainer: Veronika Romashkina -This module introduces sized list data type — 'Slist'. +This module introduces sized list data type — 'Slist'. The data type +has the following shape: + +@ +__data__ 'Slist' a = Slist + { sList :: [a] + , sSize :: 'Size' + } +@ + +As you can see along with the familiar list, it contains 'Size' field that +represents the size of the structure. Slists can be finite or infinite, and this +is expressed with 'Size'. + +@ +__data__ 'Size' + = Size 'Int' + | Infinity +@ + +This representation of the list gives some additional advantages. Getting the +length of the list is the "free" operation (runs in \( O(1) \)). This property +helps to improve the performance for a bunch of functions like 'take', 'drop', +'at', etc. But also it doesn't actually add any overhead on the existing +functions. + +Also, this allows to write a number of safe functions like 'safeReverse', +'safeHead', 'safeLast', 'safeIsSuffixOf', etc. + +== Comparison + +Check out the comparison table between lists and slists performance. + ++-------------------+--------------------+--------------------+-----------------------+-----------------------+ +| Function | list (finite) | list (infinite) | Slist (finite) | Slist (infinite) | ++===================+====================+====================+=======================+=======================+ +| 'length' | \( O(n) \) | \ | \( O(1) \) | \( O(1) \) | ++-------------------+--------------------+--------------------+-----------------------+-----------------------+ +| 'safeLast' | \( O(n) \) | \ | \( O(n) \) | \( O(1) \) | ++-------------------+--------------------+--------------------+-----------------------+-----------------------+ +| 'init' | \( O(n) \) | \ | \( O(n) \) | \( O(1) \) | ++-------------------+--------------------+--------------------+-----------------------+-----------------------+ +| 'take' | \( O(min\ i\ n) \) | \( O(i) \) | @0 < i < n@: \(O(i)\) | \(O(i)\) | +| | | | otherwise: \(O(1)\) | | ++-------------------+--------------------+--------------------+-----------------------+-----------------------+ +| 'at' | \( O(min\ i\ n) \) | \( O(i) \) | @0 < i < n@: \(O(i)\) | \( O(i) \) | +| | run-time exception | run-time exception | otherwise: \(O(1)\) | | ++-------------------+--------------------+--------------------+-----------------------+-----------------------+ +| 'safeStripPrefix' | \( O(m) \) | \( O(m) \) | \( O(m) \) | \( O(m) \) | +| | | can hang | | | ++-------------------+--------------------+--------------------+-----------------------+-----------------------+ + +== Potential usage cases + +* When you ask the length of the list too frequently. +* When you need to convert to data structures that require to know the list + size in advance for allocating an array of the elements. /Example:/ [Vector data + structure](https://hackage.haskell.org/package/vector). +* When you need to serialised lists. +* When you need to control the behaviour depending on the finiteness of the list. +* When you need a more efficient or safe implementation of some functions. + -} module Slist @@ -1771,7 +1832,7 @@ sortBy f Slist{..} = Slist (L.sortBy f sList) sSize {- | @O(n log n)@. Sorts a list by comparing the results of a key function applied to each -element. @sortOn f@ is equivalent to @'sortBy' ('comparing' f)@, but has the +element. @sortOn f@ is equivalent to @'sortBy' (comparing f)@, but has the performance advantage of only evaluating @f@ once for each element in the input list. This is called the decorate-sort-undecorate paradigm, or Schwartzian transform.