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.
Related
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
I'm writing an "append" function for a data type I've created (which basically deals with "streams"). However, this data type has 12 different constructors, dealing with different types of "stream", for example, infinite, null, fixed length, variable length, already appended etc.
There logic between the input types and output types is a bit complex but not incredibly so.
I've considered two approaches:
Match against broad categories (perhaps by wrapping in a simpler proxy type) and then match inside those matches OR
Just pattern match against 144 cases (12*12). I could perhaps reduce this to 100 with wildcard matches for particular combinations but that's about it.
I know the second approach is more ugly and difficult to maintain, but disregarding that, will GHC find the second approach easier to optimise? If it can do the second approach with a simple jump table (or perhaps two jump tables) I suspect it will be faster. But if it's doing a linear check it will be far slower.
Does GHC optimise pattern matches (even very big ones) into constant time jump tables?
Yes, GHC optimizes such pattern matches. The first seven (I think) constructors get optimizes especially well, via pointer tagging. I believe the rest will be handled by a jump table. But 144 cases sounds hard to maintain, and you'll have to watch for code size. Do you really need all those cases?
It's not too hard to write a small Haskell script that writes a huge case-block and a small benchmark for it. For example:
module Main (main) where
mapping = zip ['!'..'z'] (reverse ['!'..'z'])
test_code =
[
"module Main where",
"",
"tester :: String -> String",
"tester cs = do",
" c <- cs",
" case transform c of",
" Just c' -> [c']",
" Nothing -> [c ]",
"",
"input = concat [ [' '..'z'] | x <- [1..10000] ]",
"",
"main = print $ length $ tester $ input",
""
]
code1 =
test_code ++
[
"transform :: Char -> Maybe Char",
"transform c = lookup c " ++ show mapping
]
code2 =
test_code ++
[
"transform :: Char -> Maybe Char",
"transform c =",
" case c of"
] ++
map (\(k, v) -> " " ++ show k ++ " -> Just " ++ show v) mapping ++
[
" _ -> Nothing"
]
main = do
writeFile "Test1.hs" (unlines code1)
writeFile "Test2.hs" (unlines code2)
If you run this code, it generates two small Haskell source files: Test1.hs and Test2.hs. The former uses Prelude.lookup to map characters to characters. The latter uses a giant case-block. Both files contain code to apply the mapping to a large list of data and print out the size of the result. (This way avoids I/O, which would otherwise be the dominating factor.) On my system, Test1 takes a few seconds to run, whereas Test2 is pretty much instant.
The over-interested reader may like to try extending this to use Data.Map.lookup and compare the speed.
This proves that pattern-matching is far faster than an O(n) traversal of a list of key/value mappings... which isn't what you asked. But feel free to brew up your own benchmarks. You could try auto-generating a nested-case verses a flat-case and timing the result. My guess is that you won't see much difference, but feel free to try it.
I'm trying to hack together a new file format writer for pandoc using LaTeX.hs as a guide. Extensive use of a $$ operator is made, but I can't find this in the Haskell syntax documentation or even references to in in other projects. Here is an example:
let align dir txt = inCmd "begin" dir $$ txt $$ inCmd "end" dir
This almost looks like a concatenation operator of some kind, yet I can't make out how this is different from other concatenation operations. What is this operator, how does it work, and where is it documented?
This is a job for Hayoo or Hoogle. It's an operator defined in Text.Pandoc.Pretty.
($$) :: Doc -> Doc -> Doc infixr 5
a $$ b puts a above b.
Basically, it will make sure that a and b are on different lines, which leads to nicer LaTeX output:
\begin{dir}
txt
\end{dir}
Pandoc defines its own pretty-printing library internally but the operations (and the name of the type, Doc) are standard in Haskell pretty printing libraries. Pandoc also defines other familiars like vcat, hsep, <+> and so on; there are many pretty printing modules around, but they always support these operations.
> import Text.PrettyPrint
> text "hello" <> text "world"
helloworld
> text "hello" <+> text "world"
hello world
> text "hello" $$ text "world"
hello
world
> text "hello" <+> text "world" $$ text "goodbye" <+> text "world"
hello world
goodbye world
ghci here displays 'what the document will look like', crudely speaking.
I need your help please, my question is,
How i can make like this in haskell:
dim myVar = "text 1"
myVar += " and " + "text 2"
The first thing you need to understand about Haskell is that you don't have variables, you only have values bound to names. Variables don't exists because values are immutable. This means that they never change. To change them is a compile time error.
At first this seems like a huge disadvantage, but in time you'll come to find that in most cases you don't need mutable values (and there are ways to do mutable values, but that's an advanced topic).
So, how would you write a function to do this? You could put in a file these contents:
module Main where
appendText :: String -> String -- Takes a string, returns a string
appendText text = text ++ " and " ++ "text 2"
main :: IO () -- An IO action with no result (think none or null)
main = do
let myText = "text 1"
putStrLn (appendText myText)
Since it seems that you have not yet introduced yourself fully to Haskell, I would recommend Learn You a Haskell, my favorite tutorial for the language. It starts with the very basics and works it way up to fairly advanced topics.
I am unsure on how to use setPosition (Parsec library). Here is an extremely simple piece of code which should read the first 3 characters of the second line of a text.
import Text.ParserCombinators.Parsec
content = ["This is the first line",
"and this is the second one",
"not to talk about the third one"]
txt = unlines content
main = parseTest myPar txt
myPar = getPosition >>= \oldPos ->
let newPos = setSourceLine oldPos 2 in
setPosition newPos >>
count 3 anyChar
Still, the output is "Thi" and not "and" as I would excpect... I feel I am missing somethning very simple, but alas, I don't know what; can you help me?
The setPosition function changes what position Parsec reports for errors, but does not affect where in the stream of tokens the parsing actually is. It is used as a primitive for back-ends that need to do fancy things: preprocessors that must report positions in other files, parsers that operate on streams of non-Char tokens, and so forth.