Resistance is futile, Resistors have been dispatched to model your Resistance

Since it was getting boring to always do OOP Languages I decided to dabble in the functional realm of programming.

For that I chose the narrowly missed community-challenge by Edward: Resistor Mania, since it really is a quick-to solve using recursive and functional style.

Challenge description:

In electronics, two resistors in series have a combined resistance $R_1+R_2$, and two resistors in parallel have combined resistance of $\displaystyle \frac{R_1 R_2}{R_1 + R_2}$. Given an infinite supply of $270\Omega$ resistors with $5\%$ tolerance, write a program to describe how to combine them into any arbitrary resistance value.

To make things a little easier for me, I decided to ditch the tolerance for this first prototype. Instead of $270\Omega$ I also ask the user to provide the values for our resistors.

The code works as expected for rather simple inputs, and I didn't check for more complicated stuff yet.

The ouptut comes as a wiring help that uses simple "equations" to describe parallel and combining wiring.

Output of some manual tests:

*Main> resistor_mania 60 120
"60.0+60.0"
*Main> resistor_mania 120 60
"120.0||(120.0)"
*Main> resistor_mania 120 120
"120.0"

Code:

module Main where

import System.IO

main = do
hSetBuffering stdin LineBuffering;
putStrLn "Please enter the value of resistors we use";
res_input <- getLine;
putStrLn "Please enter the value you want to model with these resistors";
target_value <- getLine;
putStrLn (show (resistor_mania (read res_input) (read target_value)));

{-
Calculate the remaining resistor-strenght necessary to get to a given target
when going in parallel to the given existing resistor.
-}
p_resistor :: Fractional a => a -> a -> a
p_resistor = (\existing target -> (1 / ((1 / target) - (1 / existing))));

{-
Resistor_Mania: Create a wiring schema for a given target resistor value
with one single available kind of resistor
-}
resistor_mania :: (Show a, Ord a, Fractional a) => a -> a -> String
resistor_mania resistor target =
if resistor == target;
then (show resistor);
else if resistor < target;
then (show resistor) ++ "+" ++ (resistor_mania resistor (target - resistor));
else (show resistor) ++ "||(" ++ (resistor_mania resistor (p_resistor resistor target)) ++ ")";

There's quite a few things that I hope can be improved here. For one I don't like how p_resistor is declared as a lambda, and resistor_mania isn't.
Another thing I don't like is the concatenation of Strings in the control structure and the type-signature of resistor_mania makes me uneasy :/

Guards

Using so many if then and else is really weird in Haskell, I suggest using the guards:

resistor_mania resistor target
| resistor == target = (show resistor)
| resistor < target = (show resistor) ++ "+" ++ (resistor_mania resistor (target - resistor))
| otherwise = (show resistor) ++ "||(" ++ (resistor_mania resistor (p_resistor resistor target)) ++ ")"

This is the standard way of listing mutually exclusive conditions in a function definition.

Semicolons

Haskell uses a two-dimensional syntax, semicolons are not needed and should be omitted as they convey no additional information, just noise.

Prefer mainstream function definitions

p_resistor = (\existing target -> (1 / ((1 / target) - (1 / existing))))

Becomes:

p_resistor existing target = (1 / ((1 / target) - (1 / existing))))

That looks maybe less cool, but surely faster to understand.

Types

the type-signature of resistor_mania makes me uneasy :/

Haskell is type-centric, so it is good that you are worrying about types.

I deleted your types and asked the compiler what types it would like for your functions.

*Main> :t p_resistor
p_resistor :: Double -> Double -> Double
*Main> :t resistor_mania
resistor_mania :: Double -> Double -> String

I think these suggested types are simpler, and I would use them instead of your bulky type declarations.

Non-closing parenthesis

$is like a parenthesis but you do not need to close it: putStrLn$ show $resistor_mania (read res_input) (read target_value) This is somewhat subjective though... • Also, 1 / ((1 / target) - (1 / existing)) is likely easier understood as (target * existing) / (existing - target) – Mokosha Jan 7 '16 at 20:26 Instead of guards, I would use resistor_mania resistor target = case compare resistor target of EQ -> show resistor LT -> show resistor ++ "+" ++ resistor_mania resistor (target - resistor) GT -> show resistor ++ "||(" ++ resistor_mania resistor (p_resistor resistor target) ++ ")" This is like a switch statement. Looking at it, I see the further refactoring of resistor_mania resistor target = show resistor ++ case compare resistor target of EQ -> "" LT -> "+" ++ resistor_mania resistor (target - resistor) GT -> "||(" ++ resistor_mania resistor (p_resistor resistor target) ++ ")" Since resistor never changes, you could do resistor_mania resistor = go where go target = show resistor ++ case compare resistor target of EQ -> "" LT -> "+" ++ go (target - resistor) GT -> "||(" ++ go (p_resistor resistor target) ++ ")" readLn instead of getLine will read the line for you after getting it. I also recommend the above use of$. (That's actually just an operator of type (a -> b) -> a -> b which applies the function on the left to the argument on the right; it has lowest operator precedence, so it's handy for avoiding parentheses.) Yes, that lambda (\) is unnecessary.

For fun, you could replace the () by $in the output. Then you could actually implement this in terms of unfoldr: resistor_mania resistor = supersperse (show resistor) .: unfoldr$
\target -> case compare resistor target of
EQ -> Nothing
LT -> Just (" + $", target - resistor) GT -> Just (" ||$ ", p_resistor resistor target)

(.:) :: (c -> d) -> (a -> b -> c) -> a -> b -> d
(f .: g) x = f . g x

supersperse :: [a] -> [[a]] -> [a]
supersperse xs xss = xs ++ intercalate xs xss ++ xs
• aaaand I've got no idea what you're doing there in the last code-block. I think there is an explanation, but... I have no clue what the heck happens there... also I'm not sure how the output changes you mention are helping me :/ – Vogel612 Jan 5 '16 at 21:08