How to implement a placeholder in a string haskell - haskell

How can I create a function that would print sentences that have placeholders/variables in the missing spaces and then take user input and print a complete sentence by inserting the values into the placeholders.
I'm trying to print multiple sentences(statements) with missing words which then give the user the option to select 4 choices of various words to complete the sentence. The goal is to have variables added within the missing spaces that acts as placeholders which will then take the user input, and store the value into the variables where words are missing to then print a complete sentence. I have explained this further in the "What I have in mind section"
Eg. This sentence ____ not _____.
Options:
1. Is, complete
2. is, finished
3. etc
4. etc
What I have in mind
I'm trying to achieve something along the lines of using a placeholder for those spaces with no text where I can then assign values in said placeholder after the user selects the choice.
someFunction that prints sentence
"This sentence is $a not $b."
Where $a and $b and variables/placeholders that I can call later to
store the words that the user selects.
someFunction that takes the sentence from the first function and takes the options that are printed from another function, and place the value into the placeholders to make the sentence complete.
What I did
My implementation is done in a way that is just rewriting the entire sentence with each possible option that the user can choose, and then printing the sentence that matches the option that the user chooses. This doesn't give me the functional approach that I am looking for, since it's more or a hard coded approach which can get tedious when using multiple sentences which will then have the 4 options for each sentence.
My Code
--First Version
import Data.List
import System.IO
main :: IO()
sentences = do
putStrLn "The Cat is ______ from ______ the city \n"
putStrLn "Here are your options:"
putStrLn "A. big, nearby"
putStrLn "B. Nearby, in"
putStrLn "C: You, By"
putStrLn "D: By, Yourself"
option <- getChar
if (option == 'A' || option == 'a')
then putStrLn "The Cat is big from nearby the city"
else if (option == 'B' || option == 'b')
then putStrLn "The Cat is nearby from in the city"
else putStrLn "Error"
main = sentences

I like using string-interpolate for this because it's exception-safe. Then you can use Maybe's fold maybe to replace your if:
{-# LANGUAGE QuasiQuotes #-}
module Main where
import Control.Monad (forM_)
import Data.Char (toUpper)
import Data.List (lookup)
import Data.Maybe (maybe)
import Data.String.Interpolate ( i )
import System.IO
sentence :: (String, String) -> String
sentence (word1, word2) = [i|The Cat is #{word1} from #{word2} the city \n|]
type Choice = (Char, (String, String))
choices :: [Choice]
choices = [ ('A', ("big" , "nearby"))
, ('B', ("Nearby", "in"))
, ('C', ("You" , "By"))
, ('D', ("By" , "Yourself"))
]
presentChoice :: Choice -> IO ()
presentChoice (option, (word1, word2)) =
putStrLn [i|#{option}. #{word1}, #{word2}|]
sentences = do
putStrLn $ sentence ("______", "______")
putStrLn "Here are your options:"
forM_ choices presentChoice
option <- fmap toUpper getChar
putStrLn $ maybe "Error" sentence $ lookup option choices
main :: IO()
main = sentences

Related

String Formatting columns in Haskell without Text.Printf

I am new to Haskell. I am at the last part of a school project. I have to take tuples and print them to an outfile and separate them by a tab column. So (709,4226408), (12965,4226412) and (5,4226016) should have and output of
709 4226408
12965 4226412
5 4226016
What I have been trying to do is this:
genOutput :: (Int, Int) -> String
genOutput (a,b) = (show a) ++ "\t" ++ (show b)
And this gives outputs like:
"709\t4226408"
"12965\t4226412"
"5\t4226016"
There are 3 things wrong with this. 1) Quotes still appear in the output. 2) The \t tab does not actually become a tab space. .Whenever I try to make an actual tab for the "" it just comes out as a " " space. 3) They are not aligned into columns like the above example. I know Text.Printf exists but we are not allowed to import anything other than:
import System.IO
import Data.List
import System.Environment
that's the output you get from GHCi I guess? Try to use putStrLn instead:
Prelude> genOutput (1,42)
"1\t42"
Prelude> putStrLn $ genOutput (1,42)
1 42
Why is that?
If you tell GHCi to evaluate an expression it will do so and (more or less) output it using show - show is designed to work with read and will usually output a value as if you would input it directly into Haskell. For a String that will include escape sequences and the "s
Now using putStrLn it will take the string and print it to stdout as you would expect.
Using print
Another reason could be that you use print to output your value - print is show + putStrLn so it'll show the values first re-introducing the escapes (as GHCi would) - so if you use print change it to putStrLn if you are using Strings

Implementation of Recursive Functions in Haskell

I am learning haskell at the moment and I'm having troubles with my logic, especially how to go about performing certain actions.
What I'm trying to do
The main goal is to have a function that contains multiple sentences (statements) with missing words, but, it only needs to print one sentence at a time.
We also need to have another function that prints out 4 options that will fill out a complete sentence for each statement that the first function contains/prints (These options are then chosen by the user to fill out the sentence).
Eg. - "This ___ contains ____"
Options:
String, words
List, options
etc
etc
Now we need another function that will take each sentence from function 1, and the options that the user selects from function 2, and create a complete sentence and returns it to the user.
I'm unsure whether we need a separate function that will take the user input and store the value that they choose from function 2 to complete the sentence from function 1, or if we can just add this in function 2 also.
What I was able to create
I was able to perform the actions stated above, but I was only able to do this for one sentence, and all of the actions were in a single function (not making the code efficient and reusable in my opinion).
I tried again with the structure I have in mind (Shown Above) but I am stuck as I said with the logic and unsure of how to go about it. Below I have the 2 versions of code I did, The first version is showcasing what I am aiming to do, but I was only able to do it with one sentence, and the second version is where I am right now with trying to use multiple functions, but I'm unsure of where to go next
Here's the code for the first version
--First Version
import Data.List
import System.IO
main :: IO()
sentences = do
putStrLn "The Cat is ______ from ______ the city \n"
putStrLn "Here are your options:"
putStrLn "A. big, nearby"
putStrLn "B. Nearby, in"
putStrLn "C: You, By"
putStrLn "D: By, Yourself"
option <- getChar
if (option == 'A' || option == 'a')
then putStrLn "The Cat is big from nearby the city"
else if (option == 'B' || option == 'b')
then putStrLn "The Cat is nearby from in the city"
else putStrLn "Error"
main = sentences
Here's the code for the second version (Where I am)
import Data.List
import System.IO
main :: IO()
--This function contains all the sentences
sentences = do
putStrLn "\nThe Cat is ______ from the ______ \n"
putStrLn "\nThe Cow belongs to ______ from ______ ______ \n"
putStrLn "\nThe Man lives in ______ and is neighbours with ______ \n"
-- This function basically prints after each sentence is displayed to signal to the user that they need to select an option
optionsText = do
putStrLn "Here are your options: \n"
-- These Functions contain the different options for different sentences
options1 = do
putStrLn "A. Running, dog"
putStrLn "B. Hiding, Man"
putStrLn "C. Eating, Trash"
putStrLn "D. Calling, Roof"
options2 = do
putStrLn "A. Tom, Next, Door"
putStrLn "B. Rick, My, Neighbour"
putStrLn "C. Man, farm, place"
putStrLn "D. Sheltor, Animal, Factory"
option3 = do
putStrLn "A. Australia, Me"
putStrLn "B. UK, Actor"
putStrLn "C. Florida, Tom"
putStrLn "D. House, Dog"
This seems to be a question about data modelling. Your solution so far operates on the level of characters: You define strings where a specific character, _ acts as a placeholder, and where you want to insert other characters in this place.
Programming is about abstraction. Thus, take a step back and think of your problem not in terms of individual characters, but in terms of sentence fragments and placeholders. Sentence fragments and placeholders make up a sentence. Define data types for these three. Then, look at the functionality you need. You want functions that render sentence fragments and placeholders for display on screen, and you need a function that can combine sentence fragments and placeholders into sentences. Finally, you need a function to display an entire sentence in terms of the display functions of the fragments and placeholders.
Once you have this abstraction in place, you can replace a placeholder by another sentence fragment and display it.

How to search a pattern from a file/String in Haskell

** old**
Suppose we have a pattern ex. "1101000111001110".
Now I have a pattern to be searched ex. "1101". I am new to Haskell world, I am trying it at my end. I am able to do it in c but need to do it in Haskell.
Given Pattern := "1101000111001110"
Pattern To Be Searched :- "110
Desired Output:-"Pattern Found"`
** New**
import Data.List (isInfixOf)
main = do x <- readFile "read.txt"
putStr x
isSubb :: [Char] -> [Char] -> Bool
isSubb sub str = isInfixOf sub str
This code reads a file named "read", which contains the following string 110100001101. Using isInfixOf you can check the pattern "1101" in the string and result will be True.
But the problem is i am not able to search "1101" in the string present in "read.txt".
I need to compare the "read.txt" string with the user provided string. i.e
one string is their in the file "read.txt"
and second string user will provid (user defined) and we will perform search and find whether user defined string is present in the string present in "read.txt"
Answer to new:
To achieve this, you have to use readLn:
sub <- readLn
readLn accepts input until a \n is encountered and <- binds the result to sub. Watch out that if the input should be a string you have to explicitly type the "s around your string.
Alternatively if you do not feel like typing the quotation marks every time, you can use getLine in place of readLn which has the type IO String which becomes String after being bound to sub
For further information on all functions included in the standard libraries of Haskell see Hoogle. Using Hoogle you can search functions by various criteria and will often find functions which suit your needs.
Answer to old:
Use the isInfixOf function from Data.List to search for the pattern:
import Data.List (isInfixOf)
isInfixOf "1101" "1101000111001110" -- outputs true
It returns true if the first sequence exists in the second and false otherwise.
To read a file and get its contents use readFile:
contents <- readFile "filename.txt"
You will get the whole file as one string, which you can now perform standard functions on.
Outputting "Pattern found" should be trivial then.

Adding the possibility to write a AST-file to my (rail-)compiler

I'm writing rail-compiler (rail is an esoteric language) in Haskell and I get some problems within the main-function of my mainmodule.
1) I want my program to ask wheter I want to run the compiling-pipeline or simply stop after the lexer and write the AST to a file so another compiler can deal with my AST (Abstract Synatx Tree). Here is my program:
module Main (
main -- main function to run the program
)
where
-- imports --
import InterfaceDT as IDT
import qualified Testing as Test
import qualified Preprocessor as PreProc
import qualified Lexer
import qualified SyntacticalAnalysis as SynAna
import qualified SemanticalAnalysis as SemAna
import qualified IntermediateCode as InterCode
import qualified CodeOptimization as CodeOpt
import qualified Backend
-- functions --
main :: IO()
main = do putStr "Enter inputfile (path): "
inputfile <- getLine
input <- readFile inputfile
putStr "Enter outputfile (path): "
outputfile <- getLine
input <- readFile inputfile
putStr "Only create AST (True/False): "
onlyAST <- getLine
when (onlyAST=="True") do putStrLn "Building AST..."
writeFile outputfile ((Lexer.process . PreProc.process) input)
when (onlyAST=="False") do putStrLn ("Compiling "++inputfile++" to "++outputfile)
writeFile outputfile ((Backend.process . CodeOpt.process . InterCode.process . SemAna.process . SynAna.process . Lexer.process . PreProc.process) input)
I get an error in Line 21 (input <- readFile inputfile) caused by the <-. Why?
How should I do it?
2) Next thing is that I want to refactor the program in that way, that I can call it from the terminal with parameters like runhaskell Main(AST) (in that way it should just create the AST) or like runhaskell Main.hs (in that way it should do the whole pipeline).
I hope for your help!
For your error in (1), your program doesn't look syntactically incorrect at line 21 to me. However an error at <- would happen if that line were indented differently from the previous one. I suspect that you are having an indentation error due to mixing tabs and spaces in a way that looks correct in your editor but disagrees with Haskell's interpretation of tabs. The simplest recommendation is to always use spaces and never tabs.
You also have an extra copy of that line later, which you might want to remove.
I also suspect you may need to use hFlush stdin after your putStr's, for them to work as prompts.
For (2), I'd suggest using a library for proper command line argument and option parsing, such as System.Console.GetOpt which is included with GHC, or one of the fancier ones which you can find on Hackage.

BioHaskell: Read FASTA file

Using BioHaskell, how can I read a FASTA file containing aminoacid sequences?
I want to be able to:
Get a list of String sequences
Get a Map String String (from Data.Map ) from the FASTA comment (assumed to be unique) to the sequence String
Use the sequences in algorithms implemented in BioHaskell.
Note: This question intentionally does not show research effort as it was immediately answered in a Q&A-style manner.
Extracting raw sequence strings
We will assume from now on that the file aa.fa contains some aminoacid FASTA sequences. Let's start with a simple example that extracts a list of sequences.
import Bio.Sequence.Fasta (readFasta)
import Bio.Sequence.SeqData (seqdata)
import qualified Data.ByteString.Lazy.Char8 as LB
main = do
sequences <- readFasta "aa.fa"
let listOfSequences = map (LB.unpack . seqdata) sequences :: [String]
-- Just for show, we will print one sequence per line here
-- This will basically execute putStrLn for each sequence
mapM_ putStrLn listOfSequences
readFasta returns IO [Sequence Unknown]. Basically that means there is no information about whether the sequences contain Aminoacids or nucleotides.
Note that we use LB.unpack instead of show here, because show adds double quotes (") at the beginning and the end of the resulting String. Using LB.unpack works, because in the current BioHaskell version 0.5.3., SeqData is just defined as lazy ByteString.
We can fix this by using castToAmino or castToNuc:
Converting to AA/Nucleotide sequences
let aaSequences = map castToAmino sequences :: [Sequence Amino]
Note that those function currently (BioHaskell version 0.5.3) do not perform any validity checks. You can use the [Sequence Amino] or [Sequence Nuc] in the BioHaskell algorithms.
Lookup sequence by FASTA header
We will now assume that our aa.fa contains a sequence
>abc123
MGLIFARATNA...
Now, we will build a Map String String (we will use Data.Map.Strict in this example) from the FASTA file. We can use this map to lookup the sequence.
The lookup will yield a Maybe String. The intended behaviour in this example is to print the sequence if it was found, or not to print anything if nothing was found in the Map.
As Data.Maybe is a Monad, we can use Data.Foldable.mapM_ for this task.
import Bio.Sequence.Fasta (readFasta)
import Bio.Sequence.SeqData (Sequence, seqdata, seqheader)
import qualified Data.ByteString.Lazy.Char8 as LB
import Data.Foldable (mapM_)
import qualified Data.Map.Strict as Map
-- | Convert a Sequence to a String tuple (sequence label, sequence)
sequenceToMapTuple :: Sequence a -> (String, String)
sequenceToMapTuple s = (LB.unpack $ seqheader s, LB.unpack $ seqdata s)
main = do
sequences <- readFasta "aa.fa"
-- Build the sequence map (by header)
let sequenceMap = Map.fromList $ map sequenceToMapTuple sequences
-- Lookup the sequence for the "abc123" header
mapM_ print $ Map.lookup "abc123" sequenceMap
Edit: Thanks to #GabrielGonzalez suggestion, the final example now uses Data.Foldable.mapM_ instead of Data.Maybe.fromJust

Resources