0

Few questions on the below code to find if a list is sorted or not:

Why did we use lambda as key here ? Does it always mean key of a list can be derived so ?

In the enumerate loop , why did we compare key(el) < key(lst[i]) and not key(el) <key(el-1) or lst[i+1] <lst[i] ?

def is_sorted(lst, key=lambda x:x):
    for i, el in enumerate(lst[1:]):
        if key(el) < key(lst[i]): # i is the index of the previous element
            return False
    return True

hh=[1,2,3,4,6]
val = is_sorted(hh)
print(val)  

(NB: the code above was taken from this SO answer)

ekhumoro
  • 115,249
  • 20
  • 229
  • 336
Zuckerberg
  • 205
  • 6
  • 15
  • 1
    I don't understand your third question. What would be the point of comparing key(el) to itself? – Daniel Roseman Apr 29 '18 at 18:47
  • @DanielRoseman -edited some text got cutoff. – Zuckerberg Apr 29 '18 at 18:49
  • 1
    that's because list slicing makes index shifted. Clumsy way of doing it if you ask me. – Jean-François Fabre Apr 29 '18 at 18:52
  • @ekhumoro - I was looking at solution for this and this is the solution some tutorial gave me . I am looking for something that is more logical to remember . – Zuckerberg Apr 29 '18 at 18:54
  • So you have a problem and instead of telling the problem you tell us the solution you found for it .. thats sounds like a [xy-problem](https://mywiki.wooledge.org/XyProblem) - what is the problem you want to solve? – Patrick Artner Apr 29 '18 at 18:58
  • `list` objects don't have keys. Certain functions can take a [*key function*](https://docs.python.org/3/howto/sorting.html#key-functions) as a parameter. A key function is called on each element before comparing them for sorting purposes. – juanpa.arrivillaga Apr 29 '18 at 19:14

4 Answers4

1

enumerate yields both index and current element:

for i, el in enumerate(lst):
    print(i,el)

would print:

0 1
1 2
2 3
3 4
4 6

By slicing the list off by one (removing the first element), the code introduces a shift between the index and the current element, and it allows to access by index only once (not seen as pythonic to use indexes on lists when iterating on them fully)

It's still better/pythonic to zip (interleave) list and a sliced version of the list and pass a comparison to all, no indices involved, clearer code:

import itertools

def is_sorted(lst, key=lambda x:x):
    return all(key(current) < key(prev) for prev,current in zip(lst,itertools.islice(lst,1,None,None)))

The slicing being done by islice, no extra list is generated (otherwise it's the same as lst[1:])

The key function (here: identity function by default) is the function which converts from the value to the comparable value. For integers, identity is okay, unless we want to reverse comparison, in which case we would pass lambda x:-x

Jean-François Fabre
  • 137,073
  • 23
  • 153
  • 219
1

This code scans a list to see if it is sorted low to high. The first problem is to decide what "low" and "high" mean for arbitrary types. Its easy for integers, but what about user defined types? So, the author lets you pass in a function that converts a type to something whose comparison works the way you want.

For instance, lets say you want to sort tuples, but based on the 3rd item which you know to be an integer, it would be key=lambda x: x[2]. But the author provides a default key=lamba x:x which just returns the object its supplied for items that are already their own sort key.

The second part is easy. If any item is less than the item just before it, then we found an example where its not low to high. The reason it works is literally in the comment - i is the index of the element directly preceding el. We know this because we enumerated on the second and following elements of the list (enumerate(lst[1:]))

Jean-François Fabre
  • 137,073
  • 23
  • 153
  • 219
tdelaney
  • 73,364
  • 6
  • 83
  • 116
0

The point is not that the lambda "derives" the key of a list. Rather, it's a function that allows you to choose the key. That is, given a list of objects of type X, what attribute would you use to compare them with? The default is the identity function - ie use the plain value of each element. But you could choose anything here.

You could indeed write this function by comparing lst[i+1] < lst[i]. You couldn't however write it by comparing key(el) < key(el-1), because el is the value of the element itself, not the index.

Daniel Roseman
  • 588,541
  • 66
  • 880
  • 895
0

This is a function that test if a list has been sorted, as an example with the builtin sorted function. This function takes an keyword argument key which is used on every single element on the list to compute its compare value:

>>> sorted([(0,3),(1,2),(2,1),(3,0)])
[(0, 3), (1, 2), (2, 1), (3, 0)]
>>> sorted([(0,3),(1,2),(2,1),(3,0)],key=lambda x:x[1])
[(3, 0), (2, 1), (1, 2), (0, 3)]

The key keyword in your function is to be able to mimic the behavior of sorted:

>>> is_sorted([(0,3),(1,2),(2,1),(3,0)])
True
>>> is_sorted([(0,3),(1,2),(2,1),(3,0)],key=lambda x:x[1])
False

The default lambda is just there to mimic a default behavior where nothing is changed.

MegaIng
  • 7,361
  • 1
  • 22
  • 35