initial commit
This commit is contained in:
commit
69c54f4e56
22
.gitignore
vendored
Normal file
22
.gitignore
vendored
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
#### joe made this: https://goel.io/joe
|
||||||
|
|
||||||
|
#####=== Haskell ===#####
|
||||||
|
|
||||||
|
dist
|
||||||
|
cabal-dev
|
||||||
|
*.o
|
||||||
|
*.hi
|
||||||
|
*.chi
|
||||||
|
*.chs.h
|
||||||
|
*.dyn_o
|
||||||
|
*.dyn_hi
|
||||||
|
.virtualenv
|
||||||
|
.hpc
|
||||||
|
.hsenv
|
||||||
|
.cabal-sandbox/
|
||||||
|
cabal.sandbox.config
|
||||||
|
cabal.config
|
||||||
|
*.prof
|
||||||
|
*.aux
|
||||||
|
*.hp
|
||||||
|
|
20
LICENSE
Normal file
20
LICENSE
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
Copyright (c) 2016 Mahdi Dibaiee
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of this software and associated documentation files (the
|
||||||
|
"Software"), to deal in the Software without restriction, including
|
||||||
|
without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included
|
||||||
|
in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||||
|
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||||
|
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||||
|
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
27
Node.hs
Normal file
27
Node.hs
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
module Node
|
||||||
|
( createLeaf, Node(..), prettyPrint ) where
|
||||||
|
import qualified Data.Map as Map
|
||||||
|
import Data.Maybe
|
||||||
|
|
||||||
|
data Node = Node { symbol :: Maybe Char
|
||||||
|
, weight :: Float
|
||||||
|
, left :: Maybe Node
|
||||||
|
, right :: Maybe Node
|
||||||
|
} deriving (Show)
|
||||||
|
|
||||||
|
createLeaf :: Char -> Float -> Node
|
||||||
|
createLeaf c a = Node { symbol = Just c
|
||||||
|
, weight = a
|
||||||
|
, left = Nothing
|
||||||
|
, right = Nothing }
|
||||||
|
|
||||||
|
prettyPrint :: Node -> Int -> String
|
||||||
|
prettyPrint Node { weight = w, symbol = s, left = l, right = r } x =
|
||||||
|
let spaces = replicate (x * 2) ' '
|
||||||
|
indent = "\n" ++ spaces
|
||||||
|
x' = x + 1
|
||||||
|
ws = "Weight: " ++ show w
|
||||||
|
ss = if isJust s then "Symbol: " ++ (show . fromJust) s else ""
|
||||||
|
ls = if isJust l then indent ++ "Left: " ++ prettyPrint (fromJust l) x' else ""
|
||||||
|
rs = if isJust r then indent ++ "Right: " ++ prettyPrint (fromJust r) x' else ""
|
||||||
|
in ws ++ " " ++ ss ++ ls ++ rs
|
6
README.md
Normal file
6
README.md
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
hs-huffman
|
||||||
|
==========
|
||||||
|
|
||||||
|
[Huffman Coding](https://en.wikipedia.org/wiki/Huffman_coding) implementation in Haskell.
|
||||||
|
|
||||||
|
I'm just learning Haskell, so you might not want to use this in any product. If you think I'm doing it wrong, I would be glad to know!
|
24
hs-huffman.cabal
Normal file
24
hs-huffman.cabal
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
-- Initial hs-huffman.cabal generated by cabal init. For further
|
||||||
|
-- documentation, see http://haskell.org/cabal/users-guide/
|
||||||
|
|
||||||
|
name: hs-huffman
|
||||||
|
version: 0.1.0.0
|
||||||
|
-- synopsis:
|
||||||
|
-- description:
|
||||||
|
license: MIT
|
||||||
|
license-file: LICENSE
|
||||||
|
author: Mahdi Dibaiee
|
||||||
|
maintainer: mdibaiee@aol.com
|
||||||
|
-- copyright:
|
||||||
|
category: Data
|
||||||
|
build-type: Simple
|
||||||
|
-- extra-source-files:
|
||||||
|
cabal-version: >=1.10
|
||||||
|
|
||||||
|
library
|
||||||
|
-- exposed-modules:
|
||||||
|
-- other-modules:
|
||||||
|
-- other-extensions:
|
||||||
|
build-depends: base >=4.8 && <4.9
|
||||||
|
-- hs-source-dirs:
|
||||||
|
default-language: Haskell2010
|
81
index.hs
Normal file
81
index.hs
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
module Huffman ( tree, charSequence, decode )
|
||||||
|
where
|
||||||
|
import Data.List
|
||||||
|
import Data.Function (on)
|
||||||
|
import Node
|
||||||
|
import qualified Data.Map as Map
|
||||||
|
import Data.Maybe
|
||||||
|
import Debug.Trace
|
||||||
|
import Data.Char (chr)
|
||||||
|
|
||||||
|
pEOF = chr 999
|
||||||
|
|
||||||
|
-- Create initial nodes (forest of trees) for each symbol
|
||||||
|
createNodes :: String -> [Node]
|
||||||
|
createNodes s = map (\ch -> createLeaf ch $ charWeight s ch) $ nub s
|
||||||
|
|
||||||
|
-- Coding table generation step (recursive)
|
||||||
|
step :: [Node] -> Node
|
||||||
|
step nodes = let cut = (tail . tail) sorted
|
||||||
|
in
|
||||||
|
if length nodes > 1 then
|
||||||
|
step (merge:cut)
|
||||||
|
else
|
||||||
|
head nodes
|
||||||
|
where
|
||||||
|
sorted = sortBy (compare `on` weight) nodes
|
||||||
|
merge = Node { symbol = Nothing, weight = sumWeights, left = Just first, right = Just second }
|
||||||
|
first = head sorted
|
||||||
|
second = (head . tail) sorted
|
||||||
|
sumWeights = weight first + weight second
|
||||||
|
tree :: String -> Node
|
||||||
|
tree = step . createNodes
|
||||||
|
|
||||||
|
-- Root-to-leaf search, find a character's sequence string
|
||||||
|
charSequence :: Node -> Char -> String
|
||||||
|
charSequence node ch =
|
||||||
|
fromJust $ helper node ch ""
|
||||||
|
where
|
||||||
|
helper (Node { symbol = s, left = l, right = r }) ch sequ
|
||||||
|
| (isJust s) && (fromJust s == ch) = Just sequ
|
||||||
|
| isNothing l && isNothing r = Nothing
|
||||||
|
| otherwise = let leftPath = helper (fromJust l) ch (sequ ++ "0")
|
||||||
|
rightPath = helper (fromJust r) ch (sequ ++ "1")
|
||||||
|
in if isJust leftPath then leftPath else rightPath
|
||||||
|
|
||||||
|
-- Root-to-leaf search, find a character based on sequence string
|
||||||
|
findChar :: Node -> String -> Maybe Char
|
||||||
|
findChar n@(Node { symbol = s, left = l, right = r}) sequ
|
||||||
|
| length sequ > 0 = let path = if head sequ == '0' then l else r
|
||||||
|
in if isJust path then
|
||||||
|
findChar (fromJust path) (tail sequ)
|
||||||
|
else
|
||||||
|
Nothing
|
||||||
|
|
||||||
|
| otherwise = if isJust s then s else Nothing
|
||||||
|
|
||||||
|
|
||||||
|
-- Encode an string into huffman coding
|
||||||
|
encode :: String -> String
|
||||||
|
encode input = let t = tree input
|
||||||
|
table = charTable t input
|
||||||
|
in concat $ map (\a -> fromJust $ Map.lookup a table) input
|
||||||
|
|
||||||
|
-- Character table, a Map representing each character's bit sequence
|
||||||
|
charTable :: Node -> String -> Map.Map Char String
|
||||||
|
charTable t input = Map.fromList $ map (\a -> (a, charSequence t a)) (nub input)
|
||||||
|
|
||||||
|
-- Each character's weight in string
|
||||||
|
charWeight :: String -> Char -> Float
|
||||||
|
charWeight s x = genericLength $ filter (==x) s
|
||||||
|
|
||||||
|
-- Decode a string, given the tree representing it
|
||||||
|
decode :: Node -> String -> String
|
||||||
|
decode t input = let (valid, next) = span (isNothing . findChar t) $ inits input
|
||||||
|
sequ = (head next)
|
||||||
|
ninput = (tails input) !! (length valid)
|
||||||
|
ch = fromJust $ findChar t sequ
|
||||||
|
in if length ninput > 0 then
|
||||||
|
ch:(decode t ninput)
|
||||||
|
else
|
||||||
|
[ch]
|
Loading…
Reference in New Issue
Block a user