Tuples in Haskell - haskell

I'd like to write a function that combines a unique-name in list u with a verb in list v with another unique-name in u so that i'd get 3 outputs like this:
[ ("fluffy", "loves", "monkey"), ("bunny", "feeds", "fluffy"),
("bunny", "feeds", "monkey") ]
The only thing is I don't know how to get it so that the second unique-name it gives me is different than the first. Here's my code:
let fun = [ u ++ " " ++ v ++ " " ++ u | u <- ["fluffy", "bunny", "monkey"], v <- ["eats", "feeds", "loves"]]
let funThree = take 3 (cycle fun)
Currently my output is this:
["fluffy eats fluffy","fluffy feeds fluffy","fluffy loves fluffy"]

First, let's define the names so we can use them more than once
> let names = ["fluffy", "bunny", "monkey"]
We can use names twice in a list comprehension, once to get the first unique-name u1, and again to get a second name, u2.
> let fun = [ u1 ++ " " ++ v ++ " " ++ u2 | u1 <- names, v <- ["eats", "feeds", "loves"], u2 <- names]
This results in 27 different strings
> fun
["fluffy eats fluffy","fluffy eats bunny","fluffy eats monkey","fluffy feeds fluffy","fluffy feeds bunny","fluffy feeds monkey","bunny eats fluffy","bunny eats bunny","bunny eats monkey","bunny feeds fluffy","bunny feeds bunny","bunny feeds monkey","monkey eats fluffy","monkey eats bunny","monkey eats monkey","monkey feeds fluffy","monkey feeds bunny","monkey feeds monkey"]
If "fluffy eats fluffy" is a little too wierd, we can make sure the two names are different by adding a condition, u1 /= u2.
> let fun = [ u1 ++ " " ++ v ++ " " ++ u2 | u1 <- names, v <- ["eats", "feeds", "loves"], u2 <- names, u1 /= u2]
This results in only 18 different strings.
> fun
["fluffy eats bunny","fluffy eats monkey","fluffy feeds bunny","fluffy feeds monkey","bunny eats fluffy","bunny eats monkey","bunny feeds fluffy","bunny feeds monkey","monkey eats fluffy","monkey eats bunny","monkey feeds fluffy","monkey feeds bunny"]

Related

how to properly remove banned words?

I have a line from which I want to remove all words beginning with the symbol #, I do not fully understand how to do it expressively. It is clear that you could write something like this:
Split the string into words
Use the list filter to weed out unnecessary words
But I guess I don't understand how to break lines, because in addition to the space, there are such characters as \t and \n, besides, I will lose them and can not restore the original text.
An example of what I want to get:
original string:
haha lala\n#delete_me all-ok
expected result:
haha lala\nall-ok
You might want to use Data.List.Split.split with Data.List.Split.oneOf.
It returns split words including separators, so you can rebuild text with them.
split (oneOf "xyz") "aazbxyzcxd" == ["aa","z","b","x","","y","","z","c","x","d"]
Another way to look at the problem is that we want to delete strings of non-spaces that begin with an at sign #, as well as any following spaces. We don’t want to treat line breaks or other characters specially at all. That can be expressed with a simple recursive function using span / break and dropWhile:
censor :: String -> String
censor "" = ""
censor text0 = spaces ++ nonspaces ++ censor rest
where
(spaces, text1) = span isSpace text0
(word, text2) = break isSpace text1
(nonspaces, rest)
| banned word
= ("", trim text2)
| otherwise
= (word, text2)
banned :: String -> Bool
banned ('#' : _) = True
banned _ = False
trim :: String -> String
trim = dropWhile isSpace
Consider an example:
censor " send #beans money to sam#example.com"
span returns " " and "send #beans…"
break returns "send" and " #beans…"
banned returns false for "send", so we will keep it
We recursively call censor " #beans money…"
span returns " " and "#beans money…"
break returns "#beans" and " money…"
Now banned returns true for "#beans", so we drop it and trim the rest
We recursively call censor "money…"
We keep all the remaining substrings, including sam#example.com, since it is not banned
Finally, we reach the end of the string and censor "" returns ""
The end result is this expression:
" " ++ "send" ++ " " ++ "" ++ "money" ++ " " ++ "to" ++ " " ++ "sam#example.com" ++ ""
Notice that we use a series of updates to the input string resulting in a series of variables text0, text1, text2, rest for the intermediate states. Consider how you could express this pattern using State instead.

Pretty printing in Haskell: Break outer groups when printing nested tuples

I want to pretty print an AST using Haskell and (currently) wl-pprint-annotated (willing to switch to a different library).
How can I make the renderer prefer breaking the softlines of the outer group over the softlines of the inner group?
Minimal Example
Take for example the tuple ((1234, 5678), (abcd, efgh)).
The output I want:
// line width: 10
(
(
1234,
5678
),
(
abcd,
efgh
)
)
// line width: 16
(
(1234, 5678),
(abcd, efgh)
)
// line width: 32
((1234, 5678), (abcd, efgh))
The output I get:
// line width: 10
((1234,
5678),
(abcd,
efgh))
// line width: 16
((1234, 5678), (
abcd, efgh))
// line width: 32
((1234, 5678), (abcd, efgh))
Code:
module Main where
import qualified Prelude
import Prelude hiding((<>))
import Text.PrettyPrint.Annotated.WL
main :: IO ()
main = do
putStrLn $ pp 10
putStrLn $ pp 16
putStrLn $ pp 32
pp w = "// line width: " ++ show w ++ "\n" ++
display (renderPretty 1.0 w doc) ++ "\n"
doc = pair (pair (text "1234") (text "5678"))
(pair (text "abcd") (text "efgh"))
pair x y = group (nest 2 (lparen <//> x <> comma </> y) <//> rparen)
pair x y = group (nest 2 (lparen <##> x <> comma <#> y) <##> rparen)
As ekim found out, I've mixed up </> with <#>
I found the documentation to be confusing, so let me clear it up a little.
First of all the operators </> and <#> are just sugar for line and softline.
See definitions:
x </> y = x <> softline <> y
x <#> y = x <> line <> y
My problem was that I was using softline when what I wanted was line.
Commonalities between line and softline
Both are printed as space when the whole line fits the page. Both are replaced with a line break when the line does not fit the page.
Difference between line and softline
When a group foes not fit the page, all lines of the whole group are replaced with line breaks. That's the behavior I've wanted.
When the line does not fit the page, only the last softline still fitting the page is replaced.
Not the whole group.
It's like the word wrapping in our text editors: Just breaking after the last word that fits to the page.
For example
doc = paragraph p1
paragraph = foldr (</>) mempty . map text . words
p1 = "I want to pretty print an AST using Haskell and (currently) wl-pprint-annotated (willing to switch to a different library)."
is printed as
I want to pretty print an AST using Haskell and
(currently) wl-pprint-annotated (willing to
switch to a different library).

How to change an element in [String] in Haskell?

I'm working on a program that receives as input a board game as follows:
#####
#_ ##
# ##
# #
# .#
#####
1 4 (player initial position, marked with '_')
After receiving the input, the program transforms it to a [String].
This case, it would be:
["#####", "#_ ##", "# ##", "# #", "# .#", "#####", "1 4"]
How can I access position [1,4] and transform '_' to 'o'?
Function must return initial list with that transformation.
Very important note: '_' is never displayed on input, I only used it to make clear where position [1,4] is (therefore, on input we only see a blank space, ' ')
Seems like one of those tasks you might have to solve for online coding games. As others pointed out, lists are not really suited for dealing with coordinates like this. However, if you are not able to use better libraries (like in coding games) you will have to do some more work.
Here is the code from my ghci session (transforming to a proper program is left as an exercise for the reader...):
let input = ["#####", "#_ ##", "# ##", "# #", "# .#", "#####", "1 4"]
let reverseInput = reverse input
let position = head reverseInput
let board = tail reverseInput
let posX = read $ takeWhile (/=' ') position :: Int
let posY = read $ takeWhile (/=' ') $ reverse position :: Int
let (unchangedBoard, changedBoard) = splitAt posY board
let (unchangedRow, changedRow) = splitAt posX $ head changedBoard
let newRow = unchangedRow ++ "o" ++ tail changedRow
let newBoard = unchangedBoard ++ [newRow] ++ tail changedBoard
let finalOutput = reverse newBoard
mapM_ putStrLn finalOutput
Also note this code is very brittle as it uses partial functions all over the place (tail, head, read). You could try to use pattern matching instead to make the code more robust.

Haskell nested where clauses

I am a beginner coder in haskell, while doing an exercise from the first chapter of this amazing book: http://book.realworldhaskell.org/read/getting-started.html
I came across this issue:
-- test comment
main = interact wordCount
where
wordCount input = show (ls ++ " " ++ ws ++ " " ++ cs ++ "\n")
where
ls = lines input
ws = length words input
cs = length input
wonderbox:ch01 manasapte$ runghc WC < quux.txt
WC.hs:5:9: parse error on input ‘where’
Why can I not nest my wheres ?
Since your second where is attached to the wordCount definition, it needs to be indented more than it. (Although you will still have some other errors afterward.)
Others have already answered. I will just add some more explanation.
Simplifying a bit, the Haskell indentation rule is:
Some keywords start a block of things (where,let,do,case ... of).
Find the first word after such keywords and note its indentation. Name the column it occurs the pivot column.
Start a line exactly on the pivot to define a new entry in the block.
Start a line after the pivot to continue the entry started in the previous lines.
Start a line before the pivot to end the block.
Hence,
where
wordCount input = show (ls ++ " " ++ ws ++ " " ++ cs ++ "\n")
where
ls = lines input
ws = length words input
cs = length input
Actually means
where {
wordCount input = show (ls ++ " " ++ ws ++ " " ++ cs ++ "\n")
;
where { -- same column, new entry
ls = lines input
; -- same column, new entry
ws = length words input
; -- same column, new entry
cs = length input
}
}
which treats the second where as a separate definition unrelated to wordCount. If we indent it more, it will work:
where {
wordCount input = show (ls ++ " " ++ ws ++ " " ++ cs ++ "\n")
where { -- after the pivot, same entry
ls = lines input
;
ws = length words input
;
cs = length input
}
}
the indentation was incorrect, here's the working version:
-- test comment
import Data.List
main = interact wordCount
where wordCount input = unlines $ [concat $ intersperse " " (map show [ls, ws, cs])]
where ls = length $ lines input
ws = length $ words input
cs = length input

R: combinatorial string replacement

I am on the lookout for a gsub based function which would enable me to do combinatorial string replacement, so that if I would have an arbitrary number of string replacement rules
replrules=list("<x>"=c(3,5),"<ALK>"=c("hept","oct","non"),"<END>"=c("ane","ene"))
and a target string
string="<x>-methyl<ALK><END>"
it would give me a dataframe with the final string name and the substitutions that were made as in
name x ALK END
3-methylheptane 3 hept ane
5-methylheptane 5 hept ane
3-methyloctane 3 oct ane
5-methyloctane 5 ... ...
3-methylnonane 3
5-methylnonane 5
3-methylheptene 3
5-methylheptene 5
3-methyloctene 3
5-methyloctene 5
3-methylnonene 3
5-methylnonene 5
The target string would be of arbitrary structure, e.g. it could also be string="1-<ALK>anol" or each pattern could occur several times, as in string="<ALK>anedioic acid, di<ALK>yl ester"
What would be the most elegant way to do this kind of thing in R?
How about
d <- do.call(expand.grid, replrules)
d$name <- paste0(d$'<x>', "-", "methyl", d$'<ALK>', d$'<END>')
EDIT
This seems to work (substituting each of these into the strplit)
string = "<x>-methyl<ALK><END>"
string2 = "<x>-ethyl<ALK>acosane"
string3 = "1-<ALK>anol"
Using Richards regex
d <- do.call(expand.grid, list(replrules, stringsAsFactors=FALSE))
names(d) <- gsub("<|>","",names(d))
s <- strsplit(string3, "(<|>)", perl = TRUE)[[1]]
out <- list()
for(i in s) {
out[[i]] <- ifelse (i %in% names(d), d[i], i)
}
d$name <- do.call(paste0, unlist(out, recursive=F))
EDIT
This should work for repeat items
d <- do.call(expand.grid, list(replrules, stringsAsFactors=FALSE))
names(d) <- gsub("<|>","",names(d))
string4 = "<x>-methyl<ALK><END>oate<ALK>"
s <- strsplit(string4, "(<|>)", perl = TRUE)[[1]]
out <- list()
for(i in seq_along(s)) {
out[[i]] <- ifelse (s[i] %in% names(d), d[s[i]], s[i])
}
d$name <- do.call(paste0, unlist(out, recursive=F))
Well, I'm not exactly sure we can even produce a "correct" answer to your question, but hopefully this helps give you some ideas.
Okay, so in s, I just split the string where it might be of most importance. Then g gets the first value in each element of r. Then I constructed a data frame as an example. So then dat is a one row example of how it would look.
> (s <- strsplit(string, "(?<=l|\\>)", perl = TRUE)[[1]])
# [1] "<x>" "-methyl" "<ALK>" "<END>"
> g <- sapply(replrules, "[", 1)
> dat <- data.frame(name = paste(append(g, s[2], after = 1), collapse = ""))
> dat[2:4] <- g
> names(dat)[2:4] <- sapply(strsplit(names(g), "<|>"), "[", -1)
> dat
# name x ALK END
# 1 3-methylheptane 3 hept ane

Resources