# Finding perimeters of right triangles with integer-length legs, up to a limit

I have a nested for-loop that populates a list with elements:

a = []
for i in range(1, limit+1):
for j in range(1, limit+1):
p = i + j + (i**2 + j**2)**0.5
if p <= limit:
a.append(p)


I could refactor it into list comprehension:

a = [i + j + (i**2 + j**2)**0.5
for i in range(1, limit+1)
for j in range(1, limit+1)
if i + j + (i**2 + j**2)**0.5 <= limit]


But now the same complex expression is in both parts of it, which is unacceptable. Is there any way to create a list in a functional way, but more elegantly?

I guess in Lisp I would use recursion with let. How it is done in more functional languages like Clojure, Scala, Haskell?

In Racket it's possible to bind expressions inside a for/list comprehension. I've found one solution to my problem:

[k
for i in range(1, limit+1)
for j in range(1, limit+1)
for k in [i + j + (i**2 + j**2)**0.5]
k <= limit]


I'm not sure how pythonic it is.

-
Just pointing out that you can divide limit by 2 in the range function (since you square the numbers anyway) and you'll still get the full set of results. Also, I would write a transform function that handled the math and run a list comprehension over it. –  kreativitea Dec 19 '12 at 18:56

In the general case, Jeff's answer is the way to go : generate then filter.

For your particular example, since the expression is increasing wrt both variables, you should stop as soon as you reach the limit.

def max_value(limit, i) :
"""return max positive value one variable can take, knowing the other"""
if i >= limit :
return 0
return int(limit*(limit-2*i)/(2*(limit-i)))

def collect_within_limit(limit) :
return [ i + j + (i**2 + j**2)**0.5
for i in range(1,max_value(limit,1)+1)
for j in range(1,max_value(limit,i)+1) ]


Now, providing this max_value is error prone and quite ad-hoc. We would want to keep the stopping condition based on the computed value. In your imperative solution, adding break when p>limit would do the job. Let's find a functional equivalent :

import itertools

def collect_one_slice(limit,i) :
return itertools.takewhile(lambda x: x <= limit,
(i + j + (i**2 + j**2)**0.5 for j in range(1,limit)))

def collect_all(limit) :
return list(itertools.chain(*(collect_one_slice(limit, i)
for i in range(1,limit))))

-

You can rewrite it with two separate comprehensions. One to generate the values and one to filter them out. You can use itertools.product to get the cartesian product.

a = [x for x in (i + j + (i**2 + j**2)**0.5
for i, j in itertools.product(range(1, limit+1), repeat=2))
if x <= limit]

-