2

I am trying to create Python code which sorts an input list and creates tuples with number pairs which differ by 2:

def diff2(input_list):
    input_list.sort()
    result = []
    for i in input_list:               
        for i2 in input_list:          
            if i + 2 is i2:
                result.append((i, i2))          
                break                          
            elif i + 2 < i2:                    
                break
    print(result)
    return result

My test input data:

[49, 51, 75, 77, 109, 111, 119, 121, 121, 123, 124, 126, 126, 128, 170, 172, 290, 292, 308, 310, 318, 320, 444, 446, 562, 564, 564, 566, 566, 568, 568, 570, 570, 572, 633, 635, 674, 676, 711, 713, 713, 715, 901, 903, 903,905, 963, 965, 988, 990])

The code appends the touples correctly until the pair (121, 123). After the append (and corresponding break), for some reason the outer for cycle doesn't iterate further, but it holds its value at 121 for another cycle, than continues like nothing happened.

Also after (170, 172) the code seems to continue, (when I tried it in debug it even went into the append line) but still didn't do any appends. So it basically just stops at 170,172 while I think it should continue and create other number pairs as well (290, 292), (308, 310) etc.

Are the two problems related? What am I doing wrong?

Thanks in advance for any feedback.

bouteillebleu
  • 2,456
  • 23
  • 32
Dzson
  • 29
  • 3
  • 2
    `121` is in your test list twice, so it appears twice. – Aplet123 Dec 26 '20 at 01:15
  • 5
    Side note: you should use ``==`` to check **equality** (``is`` is the **identity** operator). – Mike Scotty Dec 26 '20 at 01:18
  • 1
    The initial long run-on sentence makes your question quite hard to read. Also, it helpful if you just show us the (code) result you are getting and what you expected. – Allan Wind Dec 26 '20 at 01:19
  • 3
    @MikeScotty: That's not just a side-note: The OP has numbers outside the range of the small `int` cache in their data, so that test will not work for many of their inputs; `711 + 2 == 713` is true, but `711 + 2 is 713` will be false on the CPython reference interpreter at the very least. – ShadowRanger Dec 26 '20 at 01:39
  • 1
    From ["is" operator behaves unexpectedly with integers](https://stackoverflow.com/questions/306313/is-operator-behaves-unexpectedly-with-integers): *"**Do not use `is` to compare integers.** This isn't behavior you should have any expectations about. Instead, use `==` and `!=` to compare for equality and inequality, respectively."* – kaya3 Dec 26 '20 at 03:57

3 Answers3

1

You should use '==' operator for comparison of integers instead of 'is'. i + 2 is i2 means id(i + 2) == id(i2). As pointed out by ShadowRanger, internal representation of integers changes depending on how they were generated. Try id(290 + 2) and id(292) and you can see different results.

Duplication of (121, 123) is obvious because the input has two 121. You should remove duplicate elements first if you don't want duplicate results.

chomeyama
  • 96
  • 4
1

Problem 1 (For loop):

You says the code holds 121 for one more loop, I guess that it means you get two (121, 123) in your result. As Aplet123's comment, it is because you have two 121 in input_list, so the first 121 makes the first (121, 123) and the second 121 makes the second (121, 123), which the for loop is running totally correct. The same should happen for 126and any duplicated numbers afters.

Problem 2 (is vs ==):

There is difference for is and == as ShadowRanger's comment, you may have a look at:

Is there a difference between “==” and “is”? and “is” operator behaves unexpectedly with integers

One of the answer provide a link to the Python documentation Integer Objects:

The current implementation keeps an array of integer objects for all integers between -5 and 256, when you create an int in that range you actually just get back a reference to the existing object.

I have no knowledge on implementation details, but I guess it means that the is test will fail for number > 256 as they do not get the same reference, for example:

i = 254
i2 = 256
print(i + 2 is i2) # Output: True
i = 255
i2 = 257
print(i + 2 is i2) # Output: False
print(i + 2 == i2) # Output: True

Therefore, your loop continues but the if statement is not executed start from 290 which is greater than 256.

Suggestions:

Use set() to remove duplicates, and using in so we don't need two for loop.

def diff2(input_list):
    input_list = sorted(list(set(input_list)))
    result = []
    for i in input_list:
        if i + 2 in input_list:
            result.append((i, i+2))
    return result

Output:

[(49, 51), (75, 77), (109, 111), (119, 121), (121, 123), (124, 126), (126, 128), (170, 172), 
(290, 292), (308, 310), (318, 320), (444, 446), (562, 564), (564, 566), (566, 568), (568, 570), 
(570, 572), (633, 635), (674, 676), (711, 713), (713, 715), (901, 903), (903, 905), (963, 965), 
(988, 990)]
adamkwm
  • 1,155
  • 2
  • 6
  • 18
0

The problem with your code and the reason to why it repeats (121, 123) is because you have not removed the elements that you have already found. I have fixed this using the .remove() method.

def diff2(input_list):
  input_list.sort()
  result = []
  idx = 0
  for i in input_list:               
    for i2 in input_list:          
        if (i + 2) == i2:
            result.append((i, i2))
            input_list.remove(i2)
            break                          
  print(result)
  return result
 
diff2([49, 51, 75, 77, 109, 111, 119, 121, 121, 123, 124, 126, 126, 128, 170, 172, 290, 292, 308, 310, 318, 320, 444, 446, 562, 564, 564, 566, 566, 568, 568, 570, 570, 572, 633, 635, 674, 676, 711, 713, 713, 715, 901, 903, 903,905, 963, 965, 988, 990])

Also the following lines of code are unnecessary but if you want you can keep them.

elif i + 2 < i2:                    
    break

Explanation:

Every time the value i2 is found in the for loop, it is removed from the list input_list:

input_list.remove(i2)

Links & References:

To learn more about the .remove() method visit:

https://www.w3schools.com/python/ref_list_remove.asp

or

https://www.programiz.com/python-programming/methods/list/remove

topsoftwarepro
  • 773
  • 5
  • 19
  • 1
    I would not recommend altering input_list while iterating it. It wouldn’t work on a list like 1, 3, 5, 7 if you want 3 in the 1,3 and 3,5 tuples. – Lou Franco Dec 26 '20 at 01:57