Handling error in function in Haskell - haskell

I have a sign function, which can return an error.
signe :: Int -> Char
signe chiffre
| chiffre >= 1 && chiffre <= 9 = '+'
| chiffre == 0 = '0'
| chiffre >= -9 && chiffre <= (-1) = '-'
| otherwise = error "Erreur in the sign"
I'd like to make a simple one to return the corresponding code of the sign, but with error handling.
signes liste = [ signe x | x<-liste ]
I give you an example : For now, if I call
signes [1,3,0,-10]
it gives me
++0*** Exception: Error in sign.
I'd like to have nothing instead of Exception: ++0.

You can, and should, use Maybe in such cases:
signe chiffre
| chiffre >= 1 && chiffre <= 9 = Just '+'
....
| otherwise = Nothing -- parbleu!!
signes = mapMaybe signe
You may need to import Data.Maybe for the mapMaybe function.

A better way would be to actually use the Maybe type which lets you literally return Nothing or Just aValue. You could rewrite your function as
signe :: Int -> Maybe Char
signe chiffre
| chiffre >= 1 && chiffre <= 9 = Just '+'
| chiffre == 0 = Just '0'
| chiffre >= (-9) && chiffre <= (-1) = Just '-'
| otherwise = Nothing

The problem already seems to be answered by Ingo, but I wanted to point out that since you had an error message in the original question, perhaps "Either" would be a better choice here
signe :: Int -> Either String Char
signe chiffre
| chiffre >= 1 && chiffre <= 9 = Right'+'
| chiffre == 0 = Right '0'
| chiffre >= -9 && chiffre <= (-1) = Right '-'
| otherwise = Left "Erreur in the sign"
where you can get the filtered list with
signes liste = [ x | Right x<-map signe liste ]
Both Maybe and Either are used for error checking, Either gives you the ability to pass an "Exception" up the call chain.

Related

argc/argv problems in c

i'm extremely rusty with C and when trying to compile this i keep getting the error: "lvalue required as left operand of assignment" in each of the three nested ifs.
i'm pretty sure i'm doing some really dumb and basic mistake but i can't manage to fix it!
also the code is extremely naive, is there a better way to check the various strings in argv (they must be 6 charachters long, the first 2 charachters must be either IC, FR or RG and then the other 4 should be a numeric code) and print the correct strings?
int main(int argc, char *argv[]){
int i=1;
while(i < argc){
if(strlen(argv[i]) == 6){
if(argv[i][0] == 'I' && argv[i][1] = 'C' && argv[i][2] >= '0' && argv[i][2] <= '9' && argv[i][3] >= '0' && argv[i][3] <= '9' && argv[i][4] >= '0' && argv[i][4] <= '9' && argv[i][5] >= '0' && argv[i][5] <= '9'){
printf("%s", argv[i]);
}
if(argv[i][0] == 'F' && argv[i][1] = 'R' && argv[i][2] >= 0 && argv[i][2] <= 9 && argv[i][3] >= 0 && argv[i][3] <= 9 && argv[i][4] >= 0 && argv[i][4] <= 9 && argv[i][5] >= 0 && argv[i][5] <= 9){
printf("%s", argv[i]);
}
if(argv[i][0] == 'R' && argv[i][1] = 'G' && argv[i][2] >= 0 && argv[i][2] <= 9 && argv[i][3] >= 0 && argv[i][3] <= 9 && argv[i][4] >= 0 && argv[i][4] <= 9 && argv[i][5] >= 0 && argv[i][5] <= 9){
printf("%s", argv[i]);
}
}
i++;
}
return(0);
}
EDIT: Damn this was a pretty dumb question, wrote = instead of == in the second assignment. thanks for the help!
It's because you have a typo in your second tests in each if, having written = (assignment of a value) instead of == (test for equality).
The error message is correct but not intuitive. The compiler interprets your​ code as follows (note the extra parentheses I inserted):
if ((arg[0] == 'C' && arg[1]) = 'I') {}
This is because && binds more tightly than =. Replace the = with ==, and you're fine.
You should write a helper function:
static bool parse_number(const char *arg, char ch1, char ch2, int *pnum) {
if (arg[0] == ch1 && arg[1] == ch2 && '0' <= arg[2] && arg[2] <= '9') {
char *end;
unsigned long num = strtoul(arg + 2, &end, 10);
if (*end == '\0' && end - arg == 6) {
*pnum = (int)num;
return true;
}
}
return false;
}
Then you can use it like this:
int num;
if (parse_number(argv[i], 'I', 'C', &num)) {
printf("IC number %d\n", num);
}

Average of multiple files without considering missing values

I want to calculate the average of 15 files:- ifile1.txt, ifile2.txt, ....., ifile15.txt. Number of columns and rows of each file are same. But some of them are missing values. Part of the data looks as
ifile1.txt ifile2.txt ifile3.txt
3 ? ? ? . 1 2 1 3 . 4 ? ? ? .
1 ? ? ? . 1 ? ? ? . 5 ? ? ? .
4 6 5 2 . 2 5 5 1 . 3 4 3 1 .
5 5 7 1 . 0 0 1 1 . 4 3 4 0 .
. . . . . . . . . . . . . . .
I would like to find a new file which will show the average of these 15 fils without considering the missing values.
ofile.txt
2.66 2 1 3 . (i.e. average of 3 1 4, average of ? 2 ? and so on)
2.33 ? ? ? .
3 5 4.33 1.33 .
3 2.67 4 0.66 .
. . . . .
This question is similar to my earlier question Average of multiple files in shell where the script was
awk 'FNR == 1 { nfiles++; ncols = NF }
{ for (i = 1; i < NF; i++) sum[FNR,i] += $i
if (FNR > maxnr) maxnr = FNR
}
END {
for (line = 1; line <= maxnr; line++)
{
for (col = 1; col < ncols; col++)
printf " %f", sum[line,col]/nfiles;
printf "\n"
}
}' ifile*.txt
But I can't able to modify it.
Use this:
paste ifile*.txt | awk '{n=f=0; for(i=1;i<=NF;i++){if($i*1){f++;n+=$i}}; print n/f}'
paste will show all files side by side
awk calculates the averages per line:
n=f=0; set the variables to 0.
for(i=1;i<=NF;i++) loop trough all the fields.
if($i*1) if the field contains a digit (multiplication by 1 will succeed).
f++;n+=$i increment f (number of fields with digits) and sum up n.
print n/f calculate n/f.
awk '
{
for (i = 1;i <= NF;i++) {
Sum[FNR,i]+=$i
Count[FNR,i]+=$i!="?"
}
}
END {
for( i = 1; i <= FNR; i++){
for( j = 1; j <= NF; j++) printf "%s ", Count[i,j] != 0 ? Sum[i,j]/Count[i,j] : "?"
print ""
}
}
' ifile*
assuming file are correctly feeded (no trailing empty space line, ...)
awk 'FNR == 1 { nfiles++; ncols = NF }
{ for (i = 1; i < NF; i++)
if ( $i != "?" ) { sum[FNR,i] += $i ; count[FNR,i]++ ;}
if (FNR > maxnr) maxnr = FNR
}
END {
for (line = 1; line <= maxnr; line++)
{
for (col = 1; col < ncols; col++)
if ( count[line,col] > 0 ) printf " %f", sum[line,col]/count[line,col];
else printf " ? " ;
printf "\n" ;
}
}' ifile*.txt
I just check the '?' ...

How to write nested if statements in haskell?

I have this function "path" that takes 3 arguments:
path::String->String->String->IO()
path place1 dir place2 =
if place1 == "bedroom" && d == 'n' && place2 == "den"
then do
putStrLn "You are in a bedroom with a large, comfortable bed. It has been a long, tiresome day, and you would like nothing better than to go to sleep."
else
if place1 == "bedroom" && d == 'd' && place2 == "bed"
then describe "bed"
else
if place1 == "den" && d == 's' && place2 == "bedroom"
then describe "bedroom"
else
if place1 == "bed" && d == 'u' && place2 == "bedroom"
then describe "bedroom"
else putStrLn "Cannot go there!"
I want to know how if this is the correct way of having multiple conditions and multiple if statements?
It's not incorrect, but it is not idiomatic (i.e. customary style). Usually we prefer guards to if-then-else, like in #user5402's answer. However in your case you are also just comparing to constant literals with ==, which means the best way is to take it one step further and use pattern matching (I formatted it a bit prettier too):
path :: String -> String -> String -> IO ()
path "bedroom" "n" "den" = putStrLn "You are in a bedroom with a large, comfortable bed. It has been a long, tiresome day, and you would like nothing better than to go to sleep."
path "bedroom" "d" "bed" = describe "bed"
path "den" "s" "bedroom" = describe "bedroom"
path "bed" "u" "bedroom" = describe "bedroom"
path _ _ _ = putStrLn "Cannot go there!"
Consider using guards, e.g.:
path :: String -> String -> String -> IO ()
path place1 d place2
| place1 == "bedroom" && d == "n" && place2 == "den"
= putStrLn "You are in a bedroom ..."
| place1 == "bedroom" && d == "d" && place2 == "bed"
= describe "bed"
| place1 == "den" && d == "s" && place2 == "bedroom"
= describe "bedroom"
| place1 == "bed" && d == "u" && place2 == "bedroom"
= describe "bedroom"
| otherwise = putStrLn "Cannot go there!"
Note that String literals an enclosed in double quotes.

How to add a single special character randomly anywhere inside a string? Bash

I have this script that generates a random character between 8-16. I am confused as to how I would add a single random special character from a bank [! # # $ % ^ & * ( ) _ + ] anywhere randomly inside this string?
if [ $# -eq 0 ] then
pwdlen=$(((RANDOM % 9 ) +8))
spclen=$((RANDOM % 1))
char=(0 1 2 3 4 5 6 7 8 9 a b c d e f g h i j k l m n o p q r s t u v w x y z A B C D E F G H I J K L M N O P Q R S T U V X W Y Z)
chars=(~ ! # # $ % ^ & * - +)
#rand2=$random % 11
max=${#char[*]}
for i in `seq 1 $pwdlen`
do
let "rand=$RANDOM % $max"
str="${str}${char[$rand]}"
done
echo $str
exit 0
fi
teststring=foobarspam
specialchars='!##$%^&*()_+'
randomchar=${specialchars:RANDOM % ${#specialchars}:1}
randompos=$(( RANDOM % ( ${#teststring} + 1 ) ))
newstring=${teststring:0:randompos}${randomchar}${teststring:randompos}
You can use the following code.
#!/bin/bash
if [ $# -eq 0 ]; then
pwdlen=$(((RANDOM % 9 ) +8))
spclen=$((RANDOM % 1))
char=(0 1 2 3 4 5 6 7 8 9 a b c d e f g h i j k l m n o p q r s t u v w x y z A B C D E F G H I J K L M N O P Q R S T U V X W Y Z)
chars=('~' '!' '#' '#' '$' '%' '^' '&' '*' '-' '+')
#rand2=$random % 11
max=${#char[*]}
for i in `seq 1 $pwdlen`
do
let "rand=$RANDOM % $max"
str="${str}${char[$rand]}"
done
A=$(echo $str|wc -c) ## To get the count of
P=$((RANDOM % $A)) ## To get a random position of where to insert the character.
I=$((RANDOM % 11)) ## To get a random index number of chars array
C=${chars[$#]}
echo $str | sed 's!^\(.\{'$P'\}\).!\1\'"$C"'!' ## Inserting the special character to string in defined position
exit 0
fi
Output:
$ for i in `seq 1 10`;do ./test1;done
j^eh8BmD2H
0B01^1AN6EVw
Wu2$LLTILuDN8fSV
e^90gmHjksDo
eB7wa\#fmwf
NVAtJkmfqx~
JaHvD%uyO3rB
ncFrgyyz~UkZ
q0LLRHUNATM8DL
X%ARcXgyC1Do
I am not sure what script language are you using. I wrote a solution for you using PHP. If PHP is not what you are using, you should be able to convert the same logic to other languages and get the same results.
<?php
//This is the original string where you want to add a random character to
$org_string = 'This is My original String';
//calculates the length of the string
$org_length = strlen($org_string);
//find a random position
$pos = rand(0, $org_length-1);
//concatenate the first part of the string, random character, the remaining string
$final = substr($org_string, 0, $pos) . getOne() . substr($org_string, $pos);
//print the final value
echo $final;
//return a random string
function getOne(){
//the following string is 12 characters in length. it is all available characters that you want to select from
$str = '!##$%^&*()_+';
//return a random character
return $str[rand(0, 11)];
}
?>

Linux, improved cal, shell programming

I am working with cal for a homework assignment and i am stuck on one point.
It is unlikely that anyone is really interested in getting a gregorian
calendar for a year in the first century, a time when the gregorian
calendar didn't even exist. Use the "windowing" strategy to allow your
new cal to handle years that are not the full 4 digit year. If the
year is in the range of 0 <= year <= 50, assume the year is really
2000-2050. If the year is in the range 51 <= year <= 99, assume the
year is really 1951-1999.
file named improvedcal.sh
call the shell with sh improvedcal.sh 1 2011 for example
code
case $# in
# rando stuff
*) m=$1; y=$2 ; # 2 ags: month and year
if ((y >= 0 && y <= 50)); then
y+=2000
elif ((y >= 51 && y <= 99)); then
y+=1900
fi;;
esac
case $m in
jan*|Jan*) m=1 ;;
feb*|Feb*) m=2 ;;
mar*|Mar*) m=3 ;;
apr*|Apr*) m=4 ;;
may*|May*) m=5 ;;
jun*|Jun*) m=6 ;;
jul*|Jul*) m=7 ;;
aug*|Aug*) m=8 ;;
sep*|Sep*) m=9 ;;
oct*|Oct*) m=10 ;;
nov*|Nov*) m=11 ;;
dec*|Dec*) m=12 ;;
[1-9]|10|11|12) ;; # numeric month
0[1-9]|010|011|012) ;; # numeric month
# *) y=$m; m="" ;; # plain year
esac
/usr/bin/cal $m $y # run cal with new inputs
But this is not working for some reason does anyone have any pointers for me?
It just skips right over this part for some reason.
If you don't declare a variable and directly assign to it, then it's either a string (var=stuff) or an array (var=(element0 element1 element2)). Since y is a string, y+=2000 appends the string 2000 to the value.
You can declare y as an integer variable, then the += operator will perform an addition.
declare -i y=$2
if ((y >= 0 && y <= 50)); then
y+=2000
elif ((y >= 51 && y <= 99)); then
y+=1900
fi
Another way is to use the += operator inside an arithmetic expression:
y=$2
if ((y >= 0 && y <= 50)); then
((y+=2000))
elif ((y >= 51 && y <= 99)); then
((y+=1900))
fi
Or you can perform the arithmetic operation and assign the result:
y=$2
if ((y >= 0 && y <= 50)); then
y=$((y+2000))
elif ((y >= 51 && y <= 99)); then
y=$((y+1900))
fi
You can write all of this in a single arithmetic expression using the ? … : conditional operator:
y=$2
if ((y >= 0)); then ((y <= 50 ? y += 2000 : y <= 99 ? y+=1900 : 0)); fi
Try running your script with bash -xv, it will help you understand what is happening.
Read also Bash programming intro

Resources