Reputation: 467
I was trying to make a function that quickly calculates fractions. In my particular case there a factorials, so it s kinda easy. I wrote a make_coprime
function which makes two numbers coprime (and returns a pair, which bothers me a little: it sounds a bit too complicated, is there no way to make it return 2 numbers? Looks like there isn't, but just to be sure)
Now it is something like frac{numbers]/{numbers} and I want to make all of the upper ones coprime with all the lower ones.
So I have two lists and I want to apply my function to all possible pairs with substitution after that. This is where I failed. I couldn't even do it with one element and a list, my best was
f a [] = (a, [1])
f a x:xs = ((fst (make_coprime a x)), ((snd (make_coprime a x)):(snd (f (fst (make_coprime a x)) xs))))
(there is a parse error somewhere here, but I can't find it at all)
But I feel like I overcomplicate things. It sounds way simplier than it looks when I write it. Is there an elegant way to do this?
Upvotes: 1
Views: 118
Reputation: 120751
The parsing error is that you've not used parens around x:xs
.
Before saying any more: Haskell functions should always come with a type signature, which makes it hugely easier to reason what we're talking about and where any. I'm not sure what you want, but likely
makeCoprime :: Integer -> Integer -> (Integer,Integer)
and
f :: Integer -> [Integer] -> (Integer, [Integer])
Apart from that (and to find other parse errors, which occur before the type system ever takes action), I'd first try to write it as readable as possible. First align everything meaningfully:
f a (x:xs) = ( (fst (make_coprime a x))
, ( (snd (make_coprime a x))
: (snd (f (fst (make_coprime a x)) xs))
)
)
ok, now there are a couple of parens that are not necessary and should be omitted: the ,
always seperates everything, so you never need to write e.g. (1, (2))
: just make it (1,2)
. And function application binds more tightly than any infix operator, so you also never need to write (f x) : y
, just do f x : y
. That makes your code
f a (x:xs) = ( fst (make_coprime a x)
, snd (make_coprime a x)
: snd (f (fst (make_coprime a x)) xs)
)
which is already a good deal easier to understand. You could get rid of more parens by using the $
and .
operators, but I'll leave that.
What I would definitely not leave, as chepner suggests, are these multiple invokations of make_coprime a x
. This kind of duplication is not only code-noise, it can also make your program run very slow.
f a (x:xs) = ( fst axCoprimes
, snd axCoprimes
: snd (f (fst axCoprimes) xs)
)
where axCoprimes = make_coprime a x
Now, all you ever do with axCoprimes
is eval the fst
and snd
components separately. Thus instead of giving the tuple one name, you should immediately match the components:
f a (x:xs) = (p₀, p₁ : snd (f p₀ xs))
where (p₀,p₁) = make_coprime a x
and there you go, that looks very clear IMO.
Upvotes: 4
Reputation: 532418
You only need to call coprime a x
once; you can assign its return value to a name using a let
statement:
f a (x:xs) = let pair = make_coprime a x
in ((fst pair), ((snd pair):(snd (f (fst pair) xs))))
(Your parse error is likely due to the missing parentheses around x:xs
.)
You can simplify it more by unpacking the pair immediately instead of calling fst
and snd
repeatedly.
f a (x:xs) = let (n1,n2) = make_coprime a x
in (n1, (n2:(snd (f n1 xs))))
Upvotes: 4