How to delete all char of a string in OCaml - string

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)

Related

Am I using Boxes Package Correctly?

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 |
|-----------------------------------------------------------|

count the number of vowels in a given string F#

I need a method using List.fold that will count the number of vowels in a given string.
So far I have this method.
let vowels = ['a';'e';'i';'o';'u']
let isVowel =
fun c -> vowels |> List.contains c
let count =
String.filter isVowel
>> String.length
printfn "%A" (count "aaaa")
It works fine, but I cannot figure out how to make a List.fold one using the same isVowel method. Here is my try.
The code below does not work, its purpose is to reflect my idea. Since the fold method applies the isVowel() which returns a true/false on each char in the string, if the condition is true it will add 1 to the accumulator which is the 0 parameter. When I try to use if else insinde the anon function I get an error.
let isVowel x =
match x with
| 'a' -> true
| 'e' -> true
| 'i' -> true
| 'o' -> true
| 'u' -> true
| _ -> false
let countNumOfVowels =
List.fold (fun (isVowel) (x) -> x + 1) 0 ["aaaa"]
You are trying to fold a list but your source is actually a string.
A string can be interpreted as a sequence of chars if you use Seq.fold:
"abracadabra" |> Seq.fold (fun i c -> if isVowel c then i + 1 else i) 0
// val it : int = 5
This is what I was looking for. Thanks Gus!
let countNumOfVowels str =
List.fold (fun (x: int) (c: char) -> if (isVowel c) then x + 1 else x) 0 (Seq.toList str)
countNumOfVowels "Hello"
> countNumOfVowels "Hello";;
val it : int = 2

Convert a Haskell function to SML

I'm trying to convert a Haskell function, which displays a boolean formula, to a SML function.
The function:
data Formula
= Atom String
| Neg Formula
| Conj Formula Formula
| Disj Formula Formula
precedence :: Formula -> Int
precedence Atom{} = 4
precedence Neg {} = 3
precedence Conj{} = 2
precedence Disj{} = 1
displayPrec :: Int -> Formula -> String
displayPrec dCntxt f = bracket unbracketed where
dHere = precedence f
recurse = displayPrec dHere
unbracketed = case f of
Atom s -> s
Neg p -> "~ " ++ recurse p
Conj p q -> recurse p ++ " & " ++ recurse q
Disj p q -> recurse p ++ " | " ++ recurse q
bracket
| dCntxt > dHere = \s -> "(" ++ s ++ ")"
| otherwise = id
display :: Formula -> String
display = displayPrec 0
I' ve come so far as translating it to SML:
fun precedence(operator) =
case operator of
Atom a => 4
| Neg p => 3
| Conj(p,q) => 2
| Disj(p,q) => 1
fun displayPrec dCntxt f =
let
val dHere = precedence f
val recurse = displayPrec dHere
val unbracketed = case f of
Atom a => a
| Neg p => "~ " ^ recurse p
| Conj(p,q)=>(recurse p) ^ " & " ^ (recurse q)
| Disj(p,q)=>(recurse p) ^ " | " ^ (recurse q)
(* missing bracket function *)
in
(* bracket *) unbracketed
end
The unbracketed function works. It shows the formula without braces. The only thing that is still missing is the bracket function, which I don't know what it does and how to translate it to SML. Can someone, who is more experienced, help me with this?
That would be
val bracket =
if dCntxt > dHere
then fn s => "(" ^ s ^ ")"
else fn x => x
The function compares the precedence level of your context against the precedence level of the outer operator of your expression and decides to either insert a pair of parentheses around the given string or not.

Translate Haskell parsec to FParsec

how to translate this Haskell code:
import Text.ParserCombinators.Parsec((<|>), unexpected, lookAhead, noneOf, char)
import Control.Monad(when)
data BracketElement = BEChar Char | BEChars String | BEColl String | BEEquiv String | BEClass String
p_set_elem_char = do
c <- noneOf "]"
when (c == '-') $ do
atEnd <- (lookAhead (char ']') >> return True) <|> (return False)
when (not atEnd) (unexpected "A dash is in the wrong place in a bracket")
return (BEChar c)
to FParsec ? Preferable way is without monadic syntax to provide good performance.
Thanks in advance, Alexander.
Sorry for little misleading. I slightly corrected problem to make Haskell code compilable:
import Text.ParserCombinators.Parsec((<|>), (<?>), unexpected, lookAhead, noneOf, char)
import Control.Monad(when)
import Data.Functor.Identity
import qualified Text.Parsec.Prim as PR
-- | BracketElement is internal to this module
data BracketElement = BEChar Char | BEChars String | BEColl String | BEEquiv String | BEClass String
deriving Show
p_set_elem_char :: PR.ParsecT [Char] u Identity BracketElement
p_set_elem_char = do
c <- noneOf "]"
when (c == '-') $ do
atEnd <- (lookAhead (char ']') >> return True) <|> (return False)
when (not atEnd) (unexpected "A dash is in the wrong place in a bracket")
return (BEChar c)
Now it is possible to reproduce *p_set_elem_char* computation.
I sincerely thank all of which who helped me.
I made my own approximation, but unfortunately not so functional as it could be:
type BracketElement = BEChar of char
| BEChars of string
| BEColl of string
| BEEquiv of string
| BEClass of string
let p_set_elem_char : Parser<BracketElement, _> =
fun stream ->
let stateTag = stream.StateTag
let reply = (noneOf "]") stream
let chr = reply.Result
let mutable reply2 = Reply(BEChar chr)
if reply.Status = Error && stateTag = stream.StateTag then
reply2.Status <- Error
reply2.Error <- reply.Error
else if chr = '-' && stream.Peek() <> ']' then
reply2.Status <- Error
reply2.Error <- messageError ("A dash is in the wrong place in a bracket")
reply2
Using the BracketElement type in toyvo's answer, you could do something like
let pBEChar : Parser<_, unit> =
let c =
pchar '-' .>> followedByL (pchar ']') "A dash is in the wrong place in a bracket"
<|> noneOf "-]"
c |>> BEChar
I don't know much FParsec, but here is a naive attempt, corrected a bit for performance based on the comments:
type BracketElement =
| BEChar of char
| BEChars of string
| BEColl of string
| BEEquiv of string
| BEClass of string
let parseBEChar : Parser<BracketElement,unit> =
let okChars = noneOf "]"
let endTest =
(lookAhead (skipChar ']') >>. parse.Return(true))
<|> parse.Return(false)
let failure = fail "A dash is in the wrong place in a bracket"
parse {
let! c = okChars
if c = '-' then
let! atEnd = endTest
if not atEnd then
return! failure
else
return BEChar c
else
return BEChar c
}
Similar to what Daniel proposed, you could write that parser as
let pSetElementChar : Parser<_,unit> =
satisfy (function '-' | ']' -> false | _ -> true)
<|> (pchar '-' .>> followedByString "]")
|>> BEChar
If you want to add your custom message to the error, you could use followedByL like in Daniel's answer or you could add the message using the fail primitive
let pSetElementChar2 : Parser<_,unit> =
satisfy (function '-' | ']' -> false | _ -> true)
<|> (pchar '-' .>> (followedByString "]"
<|> fail "A dash is in the wrong place in a bracket"))
|>> BEChar
A low-level implementation can be as simple as
let pSetElementChar3 : Parser<_,unit> =
fun stream ->
let c = stream.ReadCharOrNewline()
if c <> EOS then
if c <> '-' || stream.Peek() = ']' then Reply(BEChar c)
else Reply(Error, messageError "A dash is in the wrong place in a bracket")
else
Reply(Error, unexpected "end of input")

How To Change List of Chars To String?

In F# I want to transform a list of chars into a string. Consider the following code:
let lChars = ['a';'b';'c']
If I simply do lChars.ToString, I get "['a';'b';'c']". I'm trying to get "abc". I realize I could probably do a List.reduce to get the effect I'm looking for but it seems like there should be some primitive built into the library to do this.
To give a little context to this, I'm doing some manipulation on individual characters in a string and when I'm done, I want to display the resulting string.
I've tried googling this and no joy that way. Do I need to just bite the bullet and build a List.reduce expression to do this transformation or is there some more elegant way to do this?
Have you tried
System.String.Concat(Array.ofList(lChars))
How many ways can you build a string in F#?
Here's another handful:
let chars = ['H';'e';'l';'l';'o';',';' ';'w';'o';'r';'l';'d';'!']
//Using an array builder
let hw1 = new string [|for c in chars -> c|]
//StringBuilder-Lisp-like approach
open System.Text
let hw2 =
string (List.fold (fun (sb:StringBuilder) (c:char) -> sb.Append(c))
(new StringBuilder())
chars)
//Continuation passing style
let hw3 =
let rec aux L k =
match L with
| [] -> k ""
| h::t -> aux t (fun rest -> k (string h + rest) )
aux chars id
Edit: timings may be interesting? I turned hw1..3 into functions and fed them a list of 500000 random characters:
hw1: 51ms
hw2: 16ms
hw3: er... long enough to grow a beard? I think it just ate all of my memory.
Didn't see this one here, so:
let stringFromCharList (cl : char list) =
String.concat "" <| List.map string cl
"" is just an empty string.
FSI output:
> stringFromCharList ['a'..'d'];;
val it : string = "abcd"
EDIT:
Didn't like this syntax coming back to this so here's a more canonically functional one:
['a'..'z'] |> List.map string |> List.reduce (+)
['a';'b';'c'] |> List.fold_left (fun acc c -> acc ^ (string c)) ""
Edited:
Here is yet another funny way to do your task:
type t =
| N
| S of string
static member Zero
with get() = N
static member (+) (a: t, b: t) =
match a,b with
| S a, S b -> S (a+b)
| N, _ -> b
| _, N -> a
let string_of_t = function
|N -> ""
|S s -> s
let t_of_char c = S (string c)
['a'; 'b'; 'c'] |> List.map t_of_char |> List.sum |> string_of_t
Sadly, just extending System.String with 'Zero' member does not allow to use List.sum with strings.
Edited (answer to Juilet):
Yes, you are right, left fold is slow. But i know more slow right fold :) :
#r "FSharp.PowerPack"
List.fold_right (String.make 1 >> (^)) ['a';'b';'c'] ""
and of course there is fast and simple:
new System.String(List.to_array ['1';'2';'3'])
And i used 'sprintf' seems to me easier:
let t = "Not what you might expect"
let r = [ for i in "aeiou" -> i]
let q = [for a in t do if not (List.exists (fun x -> x=a) r) then yield a]
let rec m = function [] -> "" | h::t -> (sprintf "%c" h) + (m t)
printfn "%A" (m q)
The following solution works for me:
let charList = ["H";"E";"L";"L";"O"]
let rec buildString list =
match list with
| [] -> ""
| head::tail -> head + (buildString tail)
let resultBuildString = buildString charList
[|'w'; 'i'; 'l'; 'l'|]
|> Array.map string
|> Array.reduce (+)
or as someone else posted:
System.String.Concat([|'w'; 'i'; 'l'; 'l'|])

Resources