my code is the following :
type appointment=
|Probationary of int
|Fixed
type GradeLevel =
| Junior_Dev of appointment
| Dev
| Senior_Dev
| PM
| Architect of int
type person = {gradeLevel: GradeLevel ; title: string; salary: float; name: string}
let John_C = {gradeLevel=Dev; title="Hamster..."; salary= 3500000.0; name= "John Connor"}
let James_J = {gradeLevel=Junior_Dev (Probationary 3); title="Gofer"; salary= 3500000.0; name= "James Joyce"}
let splitter (EmpList: person list) =
let rec splitter remaining (j,d,s,p,a) = //HERE IS THE LINE OF THE ERROR
match remaining with
| [] -> (j,d,s,p,a)
| x::xs ->
match x.gradeLevel with
| Junior_Dev w -> splitter xs (x::j, d, s, p, a)
| Dev -> splitter xs (j, x::d, s, p, Arch)
| Senior_Dev -> splitter xs (j, d, x::s, p, a)
| PM -> splitter xs (j, d, s, x::p, a)
| Architect w -> splitter xs (j, d, s, p, x::a)
splitter EmpList ([],[],[],[],[])
I can't seem to find out my error on that line.
Basically with splitter: I want to take an employee list and return a tuple with Gradelevel lists...So I could see all my developers and PM seperately using this function.
Anything you see wrong? Thank you
This code compiles for me:
type appointment=
| Probationary of int
| Fixed
type GradeLevel =
| Junior_Dev of appointment
| Dev
| Senior_Dev
| PM
| Architect of int
type person = {gradeLevel: GradeLevel ; title: string; salary: float; name: string}
let John_C = {gradeLevel=Dev; title="Hamster..."; salary= 3500000.0; name= "John Connor"}
let James_J = {gradeLevel=Junior_Dev (Probationary 3); title="Gofer"; salary= 3500000.0; name= "James Joyce"}
let splitter (EmpList: person list) =
let rec splitter remaining (j,d,s,p,a) =
match remaining with
| [] -> (j,d,s,p,a)
| x::xs ->
match x.gradeLevel with
| Junior_Dev w -> splitter xs (x::j, d, s, p, a)
| Dev -> splitter xs (j, x::d, s, p, a)
| Senior_Dev -> splitter xs (j, d, x::s, p, a)
| PM -> splitter xs (j, d, s, x::p, a)
| Architect w -> splitter xs (j, d, s, p, x::a)
splitter EmpList ([],[],[],[],[])
I replaced Arch in the original code with a.
Related
I am trying to convert CSV files into a table using the Boxes package
.
I am using IHaskell (Jupyter notebook extension) to do data analysis and would like to include nice looking tables. I could write a program to convert the CSV to a Jupyter markdown native table but that's not as fluid a process as generating the table on the fly.
Below is the code which produces a reasonable looking table but I had to do some manual stuff which made me think I wasn't using Boxes correctly.
There doesn't appear to be a way to set a fixed width Box (with padding) and the actual data centered within it. It's easy to get it to center the data but the box width is dependent on the width of the data resulting in a 'jagged' column.
My complicated code is below .. I've been doing Haskell for awhile but I still consider myself a novice.
Any comments greatly appreciated.
Tom
module Main (main) where
{-
Notes:
- This converst a CSV into a table
- Am I using the Boxes package but I had to do a lot
of work, using only punctuateH, punctuateV from the
package.
- Is there a better way to do it?
- Things I had to do 'manually'
- Center the data in a fixed width box. If I didn't
do this the columns would not align since the data
has various widths.
- Add the outside borders
- The input is rows of data separated by commas .. (CSV)
on,pinput,pout,eff,ledi
5.0e-6,8.43764e-2,7.88486e-2,0.934486,8.63554e-3
5.4e-5,3.04731,2.90032,0.951765,0.214559
1.03e-4,6.34257,6.03162,0.950973,0.434331
- Produces,
|--------------------------------------------------------------|
| on | pinput | pout | eff | ledi |
| ------------------------------------------------------------ |
| 5.0e-6 | 8.43764e-2 | 7.88486e-2 | 0.934486 | 8.63554e-3|
| ------------------------------------------------------------ |
| 5.4e-5 | 3.04731 | 2.90032 | 0.951765 | 0.214559 |
| ------------------------------------------------------------ |
| 1.03e-4 | 6.34257 | 6.03162 | 0.950973 | 0.434331 |
|--------------------------------------------------------------|
-}
import Text.PrettyPrint.Boxes
replace :: Eq a => a -> a -> [a] -> [a]
replace a b = map $ \c -> if c == a then b else c
setLen :: Int -> String -> String
setLen n str =
let fullPad = n - (length str)
halfPadLen = truncate $ (fromIntegral fullPad) / (2.0::Double)
halfPad = replicate halfPadLen ' '
in
if (even fullPad)
then halfPad ++ str ++ halfPad
else halfPad ++ " " ++ str ++ halfPad
padEnds :: String -> String
padEnds str =
let theLines = lines str
tableW = (length (head theLines)) + 2
allData = concatMap (\aLine -> "|" ++ aLine ++ "|\n") theLines
bar = replicate (tableW-2) '-'
in "|" ++ bar ++ "|\n" ++ allData ++ "|" ++ bar ++ "|\n"
renderTable :: String -> String
renderTable str = toStr ( lines (replace ',' ' ' str) )
where
toStr :: [String] -> String
toStr theLines =
let allBoxes = map (\aLine -> makeBoxes aLine) theLines
hs = map (\row -> punctuateH center1 (text " | ") row) allBoxes
box = punctuateV center1 (text (replicate 60 '-')) hs
rawStr = render box
in padEnds rawStr
makeBoxes :: String -> [Box]
makeBoxes aLine =
let sameLen = map (setLen 10) (words aLine)
in map text sameLen
main :: IO ()
main = do
fileData <- readFile "test2.csv"
putStrLn $ renderTable fileData
Yes, I think you're using boxes wrong. Here's what my go would look like:
import Prelude hiding ((<>)) -- ugh
import Data.List
import Text.PrettyPrint.Boxes
table :: [[String]] -> Box
table sss = border <> punctuateH center1 sep cs <> border where
border = vtext (replicate height '|')
sep = vtext (take height (cycle "-|"))
height = maxPosOn rows cs
cs = map column (transpose sss)
column :: [String] -> Box
column ss = sep // punctuateV center1 sep (map text ss) // sep where
sep = text (replicate (maxPosOn length ss+2) '-')
vtext :: String -> Box
vtext = vcat center1 . map char
maxPosOn :: (a -> Int) -> [a] -> Int
maxPosOn f = maximum . (0:) . map f
Try it out in ghci:
> printBox $ table [["on","pinput","pout","eff","ledi"],["5.0e-6","8.43764e-2","7.88486e-2","0.934486","8.63554e-3"],["5.4e-5","3.04731","2.90032","0.951765","0.214559"],["1.03e-4","6.34257","6.03162","0.950973","0.434331"]]
|-----------------------------------------------------------|
| on | pinput | pout | eff | ledi |
|-----------------------------------------------------------|
| 5.0e-6 | 8.43764e-2 | 7.88486e-2 | 0.934486 | 8.63554e-3 |
|-----------------------------------------------------------|
| 5.4e-5 | 3.04731 | 2.90032 | 0.951765 | 0.214559 |
|-----------------------------------------------------------|
| 1.03e-4 | 6.34257 | 6.03162 | 0.950973 | 0.434331 |
|-----------------------------------------------------------|
I have the following data types
data Prop =
Var Name
| Neg Prop
| Conj Prop Prop
| Disy Prop Prop
| Impl Prop Prop
| Syss Prop Prop deriving Show -- if and only if
-- And the following
type Name = String
type State = (Name, Bool) -- The state of a proposition, Example ("P", True), ("Q", True)
type States = [State] -- A list of states, [("P", True), ("Q", False), ...]
type Row = (States, Bool) -- A row of the table. ([("P", True), ("Q", False), ...], True)
type Table = [Row]
The case is that I want to generate all the possible states of a proposition
P, Q, R
1 1 1
1 1 0
1 0 1
...
To do this, I create auxiliary functions to gradually build the states
-- Get all the atoms of a proposition
varList :: Prop -> [Name]
varList (Var p) = [p]
varList (Neg p) = varList p
varList (Conj p q) = varList p ++ varList q
varList (Disy p q) = varList p ++ varList q
varList (Impl p q) = varList p ++ varList q
varList (Syss p q) = varList p ++ varList q
--Power set to get all values
conjPoten :: Eq a => [a] -> [[a]]
conjPoten [] = [[]]
conjPoten (x:xs) = map (x: ) pt `union` pt
where
pt = conjPoten xs
-- Give value to a proposition, "P" -> True, "" -> False
giveValue:: Name -> Bool
giveValue p = p /= []
-- Generate a State, "P" -> ("P",True), "" -> ("",False)
generateState :: Name -> State
generateState p = (p , daValor p)
-- The function that I want
generateStates:: [Name] -> States
generateStates p = [(a,True) | a <-p]
This, of course, is a test to verify that "it works", because if
generateStates ["P","Q", "R"] = [("P",True),("Q",True),("R",True)]
I did this thinking that in the power set we are going to have cases like ["P","Q","R"] and ["P","Q"], that is, there is not going to be "R". So the intention is that
["P","Q","R"] gives us [("Q",True),("P",True),("R",True)] and
["P","Q"] gives us [("Q",True),("P",True),("R",False)]
But from here I have two questions
The first is, that I have to modify the second element of the tuple, so what I came up with was
generateStates :: [Name] -> States
generateStates p = [ (a, b) | a<- p, a<- giveValue p]
The main error that the prelude marks me is:
Couldn't match type ‘[Char]’ with ‘Char’
Which I understand, because p is a list and giveValue works with a Name, not with a list of Names
So my question is: How do I get that p out of a Name? and that it does not stay as a list of Name
I tried to do it like
generateStates :: [Name] -> States
generateStates [p] = [ (p, b) | a<- giveValue p]
But that tells me:
Couldn't match expected type ‘[Bool]’ with actual type ‘Bool’
Which, now I don't understand, plus it tells me there aren't enough patterns
Why does this happen?
The other question is that, having
generateStates :: [Name] -> States
generateStates p = [ (a, True) | a<-p]
and try it with
generateStates ["P","Q"] would only give me [("Q",True),("P",True)]
But we have P, Q and R, so I'm missing the ("R", False)
But since it is in the arguments that we pass, it cannot add it to the list
Where do I get that R? those missing variables?
Thanks!
To change the tuple, you really create a new one, as they are not mutable. You could create a function using pattern matching. The below function works on pairs (tuples with two elements).
modTuple (firstValue, secondValue) updatedValue = (firstValue, updatedValue)
Alternatively you could access the members of the tuple with the built-in fst and snd to access the first and second elements, and create a new tuple.
You can use pattern matching to access individual elements of a list, and build up States recursively. I.e.
generateStates [] = []
generateStates (p:ps) = (p, giveValue p):(generateStates ps)
Is there an easy way to delete all occurrences of a char in an OCaml string ?
I thought I could use this clean function :
let clean =
function
| ' ' | '[' | ']' | '\n' | '>' -> ''
| x -> x
in
But you can't use '' in OCaml.
So I came with this solution :
let delete =
function
| ' ' | '[' | ']' | '\n' | '>' -> true
| _ -> false
in
let char_list, size =
String.fold_left
(fun (acc, count) c ->
if delete c then acc, count
else c::acc, count+1) ([],0) path_string
in
let char_list = ref ## List.rev char_list in
let aux() =
match !char_list with
| [] -> failwith "unexpected"
| x :: xs -> char_list := xs; x
in
let cleaned_string = String.init size (fun _ -> aux()) in
cleaned_string
But it's big - with python it's just s.replace('>','') to suppress one - and only work with OCaml 4.13 which is not supported for my installation.
If you want to build a string dynamically, you should use a buffer
let remove_chars erase s =
let b = Buffer.create 10 in
String.iter (fun c -> if not (erase c) then Buffer.add_char b c);
Buffer.contents b
(Building a list of characters is extremely inefficient.)
The closest equivalent to python might be Str.global_replace:
Str.(global_replace (regexp ">") "" s)
or to do multiple characters at once you could do something like:
Str.(global_replace (regexp "[youChar1yourChar2yourChar3]") "" s)
So in your case :
Str.(global_replace (regexp "[][> \n]") "" s)
In this code
neighbours :: CityMap -> District -> [District]
neighbours (CM (_,rs)) b = mapMaybe neighbour rs
where neighbour (p,q)
| b == p = Just q --parse error (possibly incorrect indentation or mismatched brackets)
| b == q = Just p
| otherwise = Nothing
i have parse in first «|»
The guards have to be indented further than the name of the function they're part of, for example:
neighbours :: CityMap -> District -> [District]
neighbours (CM (_,rs)) b = mapMaybe neighbour rs
where neighbour (p,q)
| b == p = Just q
| b == q = Just p
| otherwise = Nothing
This is because following the where, you're defining a (local) function neighbour, which has to also follow the layout rule; if the guard is further to the left, it's not a continuation of the definition of neighbour. You'd get the same error in a file that looked like this:
neighbour (p,q)
| b == p = Just q
Help me translate following block of the Haskell code. The run function produces text string that corresponding to a given regex that abstracted as Pattern.
Declaration of the type Pattern you can see below in the block of F# code. You can test run function like
genex $ POr [PConcat [PEscape( DoPa 1) 'd'], PConcat [PEscape (DoPa 2) 'd']]
{-# LANGUAGE RecordWildCards, NamedFieldPuns #-}
import qualified Data.Text as T
import qualified Control.Monad.Stream as Stream
import Text.Regex.TDFA.Pattern
import Control.Monad.State
import Control.Applicative
genex = Stream.toList . run
maxRepeat :: Int
maxRepeat = 3
each = foldl1 (<|>) . map return
run :: Pattern -> Stream.Stream T.Text
run p = case p of
PBound low high p -> do
n <- each [low..maybe (low+maxRepeat) id high]
fmap T.concat . sequence $ replicate n (run p)
PConcat ps -> fmap T.concat . Stream.suspended . sequence $ map run ps
POr xs -> foldl1 mplus $ map run xs
PEscape {..} -> case getPatternChar of
'd' -> chars $ ['0'..'9']
'w' -> chars $ ['0'..'9'] ++ '_' : ['a'..'z'] ++ ['A'..'Z']
ch -> isChar ch
_ -> error $ show p
where
isChar = return . T.singleton
chars = each . map T.singleton
Below I give my poor attempt. It works but incorrectly. The problem is in the following.
Let assume parse produces Pattern like that
parse "\\d\\d";;
val it : Pattern = POr [PConcat [PEscape (DoPa 1,'d'); PEscape (DoPa 2,'d')]]
and
parse "\\d{2}";;
val it : Pattern = POr [PConcat [PBound (2,Some 2,PEscape (DoPa 1,'d'))]]
So feeding both patterns to run I expect to receive seq [['2'; '2']; ['2'; '3']; ['2'; '1']; ['2'; '4']; ...] that corresponding to seq ["22"; "23"; "21"; "24"; ...] (2 symbols per string)
This is valid in the first case,
POr [PConcat [PEscape (DoPa 1,'d'); PEscape (DoPa 2,'d')]] |> run;;
val it : seq = seq [['2'; '2']; ['2'; '3']; ['2'; '1']; ['2'; '4']; ...]
seq ["22"; "23"; "21"; "24"; ...]
but not in the second
POr [PConcat [PBound (2,Some 2,PEscape (DoPa 1,'d'))]] |> run;;
val it : seq = seq [['2']; ['2']; ['2']; ['3']; ...]
seq ["2"; "2", "2"; "3", "2"; "1", "2"; "4";...] (1 symbol per string)
I tested different variants with the following clauses:
| POr ps -> Seq.concat (List.map run ps)
| PConcat ps -> (sequence (List.map (run >> Seq.concat) ps))
| PBound (low,high,p) ->
but all in vain. I can't figure out the valid translation.
-Maybe I should use String or Array instead of char list.
-And I assume that Seq is quite good analogue to Control.Monad.Stream. Is it right?
Thanks in advance for help
open System
/// Used to track elements of the pattern that accept characters or are anchors
type DoPa = DoPa of int
/// Pattern is the type returned by the regular expression parser.
/// This is consumed by the CorePattern module and the tender leaves
/// are nibbled by the TNFA module.
type Pattern = PEmpty
| POr of Pattern list // flattened by starTrans
| PConcat of Pattern list // flattened by starTrans
| PBound of int * (int option) * Pattern // eliminated by starTrans
| PEscape of DoPa * char // Backslashed Character
let maxRepeat = 3
let maybe deflt f opt =
match opt with
| None -> deflt
| Some v -> f v
/// Cartesian production
/// try in F# interactive: sequence [[1;2];[3;4]];;
let rec sequence = function
| [] -> Seq.singleton []
| (l::ls) -> seq { for x in l do for xs in sequence ls do yield (x::xs) }
let from'space'to'tilda = [' '..'~'] |> List.ofSeq
let numbers = ['0'..'9'] |> List.ofSeq
let numbers'and'alphas = (['0'..'9'] # '_' :: ['a'..'z'] # ['A'..'Z']) |> List.ofSeq
let whites = ['\009'; '\010'; '\012'; '\013'; '\032' ] |> List.ofSeq
let rec run (p:Pattern) : seq<char list> =
let chars chs = seq { yield [for s in chs -> s] }
match p with
| POr ps -> Seq.concat (List.map run ps)
| PConcat ps -> (sequence (List.map (run >> Seq.concat) ps))
| PBound (low,high,p) ->
let ns = seq {low .. maybe (low + maxRepeat) id high}
Seq.concat (seq { for n in ns do yield sequence (List.replicate n (((run >> Seq.concat) p))) })
// Seq.concat (seq { for n in ns do yield ((List.replicate n (run p)) |> Seq.concat |> List.ofSeq |> sequence)})
//((List.replicate low (run p)) |> Seq.concat |> List.ofSeq |> sequence)
// PConcat [ for n in ns -> p] |> run
| PEscape(_, ch) ->
match ch with
| 'd' -> chars numbers
| 'w' -> chars numbers'and'alphas
| ch -> chars [ch]
| _ -> Seq.empty
I don't know why you didn't translate Data.Text from Haskell to string in F#, you just need to mimic two functions. Apart from that I did just a few changes to make it work, this way you can compare it easily with your original code, see replaced code between (* *)
open System
// Mimic Data.Text as T
module T =
let concat (x:seq<_>) = System.String.Concat x
let singleton (x:char) = string x
/// Used to track elements of the pattern that accept characters or are anchors
type DoPa = DoPa of int
/// Pattern is the type returned by the regular expression parser.
/// This is consumed by the CorePattern module and the tender leaves
/// are nibbled by the TNFA module.
type Pattern = PEmpty
| POr of Pattern list // flattened by starTrans
| PConcat of Pattern list // flattened by starTrans
| PBound of int * (int option) * Pattern // eliminated by starTrans
| PEscape of DoPa * char // Backslashed Character
let maxRepeat = 3
let maybe deflt f opt =
match opt with
| None -> deflt
| Some v -> f v
/// Cartesian production
/// try in F# interactive: sequence [[1;2];[3;4]];;
let rec sequence = function
| [] -> Seq.singleton []
| (l::ls) -> seq { for x in l do for xs in sequence ls do yield (x::xs) }
let from'space'to'tilda = [' '..'~'] |> List.ofSeq
let numbers = ['0'..'9'] |> List.ofSeq
let numbers'and'alphas = (['0'..'9'] # '_' :: ['a'..'z'] # ['A'..'Z']) |> List.ofSeq
let whites = ['\009'; '\010'; '\012'; '\013'; '\032' ] |> List.ofSeq
let rec run (p:Pattern) (*: seq<char list> *) =
(* let chars chs = seq { yield [for s in chs -> s] } *)
let chars (chs:seq<char>) = Seq.map string chs
match p with
| POr ps -> Seq.concat (List.map run ps)
| PConcat ps -> Seq.map T.concat << sequence <| List.map run ps (* (sequence (List.map (run >> Seq.concat) ps)) *)
| PBound (low,high,p) ->
seq {
for n in [low..maybe (low+maxRepeat) id high] do
yield! ( (Seq.map T.concat << sequence) (List.replicate n (run p)) )}
(*let ns = seq {low .. maybe (low + maxRepeat) id high}
Seq.concat (seq { for n in ns do yield sequence (List.replicate n (((run >> Seq.concat) p))) *)
// Seq.concat (seq { for n in ns do yield ((List.replicate n (run p)) |> Seq.concat |> List.ofSeq |> sequence)})
//((List.replicate low (run p)) |> Seq.concat |> List.ofSeq |> sequence)
// PConcat [ for n in ns -> p] |> run
| PEscape(_, ch) ->
match ch with
| 'd' -> chars numbers
| 'w' -> chars numbers'and'alphas
| ch -> chars [ch]
| _ -> Seq.empty
UPDATE
If you are translating Haskell code to F# you may try using this code which mimics many Haskell functions, including those using Type Classes.
I did a test translating as close as possible to your original Haskell code but using F# List (not lazy) and looks like this:
#load "Prelude.fs"
#load "Monad.fs"
#load "Applicative.fs"
#load "Monoid.fs"
open Prelude
open Control.Monad.Base
open Control.Applicative
module T =
let concat (x:list<_>) = System.String.Concat x
let singleton (x:char) = string x
type DoPa = DoPa of int
type Pattern = PEmpty
| POr of Pattern list
| PConcat of Pattern list
| PBound of int * (int option) * Pattern
| PEscape of DoPa * char
let maxRepeat = 3
let inline each x = foldl1 (<|>) << map return' <| x
let rec run p:list<_> =
let inline isChar x = return' << T.singleton <| x
let inline chars x = each << map T.singleton <| x
match p with
| PBound (low,high,p) -> do' {
let! n = each [low..maybe (low+maxRepeat) id high]
return! (fmap T.concat << sequence <| replicate n (run p))}
| PConcat ps -> fmap T.concat << sequence <| map run ps
| POr xs -> foldl1 mplus <| map run xs
| PEscape (_, ch) ->
match ch with
| 'd' -> chars <| ['0'..'9']
| 'w' -> chars <| ['0'..'9'] # '_' :: ['a'..'z'] # ['A'..'Z']
| ch -> isChar ch
| _ -> failwith <| string p
let genex = run