Andrew S.
Andrew S.

Reputation: 467

Applying functions to lists with substitution

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

Answers (2)

leftaroundabout
leftaroundabout

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

chepner
chepner

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

Related Questions