8

I wrote this program in python to see if a list is ascending and its not working, can someone help me?

list1 = [1, 2, 3, 4]
print (list1)
length = len(list1)
run_started = False
for x in range(length - 1):
    t = list1[x + 1] - list1[x]
    if t > 0 :
        if run_started:
            run_length = x
        else:
            run_started = True
            run_length = x
    else:
        if run_started:
            print (True)
            print ("Run Length: {0}".format(run_length))
            break
if not run_started:
    print (False)
Martijn Pieters
  • 963,270
  • 265
  • 3,804
  • 3,187
user2980776
  • 93
  • 1
  • 1
  • 5
  • 3
    What exactly is wrong? `Its not working` isn't enough. – That1Guy Nov 11 '13 at 21:19
  • Can you tell us what you are receiving as output? – Alex Chumbley Nov 11 '13 at 21:22
  • is your list always sorted? and you just need to check ascending or descending? – Joran Beasley Nov 11 '13 at 21:23
  • as far as I can tell this code does work though.. it just only produces output if the list is not in ascending order. – OGHaza Nov 11 '13 at 21:29
  • its not tellimg me where it starts ascending and how long it is ascending for – user2980776 Nov 11 '13 at 21:53
  • Just an FYI: after the OP commented, it's pretty clear this is *not* a duplicate. The question here is about how to calculate the length of the run of ascending values from the start. It is *not* about how to merely check whether the array is in ascending order. I think it should be re-opened, especially since none of the answers at the supposed "duplicate" question addresses this. – ely Nov 12 '13 at 19:36

5 Answers5

22

I'd say the easiest (although not the most efficient) way would be:

list1 = [3, 1, 2, 4]

if sorted(list1) == list1:
    print "list1 is sorted"
jbat100
  • 16,658
  • 3
  • 43
  • 70
  • 3
    or just `sorted(the_list) == the_list` – Joran Beasley Nov 11 '13 at 21:25
  • good point, this is a better answer... – jbat100 Nov 11 '13 at 21:27
  • 1
    I'm tempted to down-vote this, but won't because it is technically correct. However, this is a *terrible* way to solve the problem, even when the problem is just checking that the `list` is sorted. Given that the OP actually wants more info about when the first unsorted-ascending entry occurs, it doesn't even facilitate answering the question. – ely Nov 11 '13 at 22:02
  • @EMS the OP's question is "I wrote this program in python to see if a list is ascending and its not working", so my answer is a suggestion for how to do it. He didn't state that he wanted to know the first unsorted element until the 7th comment. – jbat100 Nov 11 '13 at 22:17
10

Well, here's my go at it. Some have suggested sorting the list, which would be O(nlogn). I propose a simpler O(n) solution

def isAscending(list):
    previous = list[0]
    for number in list:
        if number < previous:
            return False
        previous = number
    return True
slider
  • 12,142
  • 1
  • 25
  • 40
5

How about a one-liner to tell if every number in the list x is strictly increasing?

[(x[k+1]-x[k])>0 for k in range(len(x)-1)].count(True) == len(x)-1
Greg von Winckel
  • 2,171
  • 2
  • 15
  • 14
1

I think the OP is asking what is wrong with their own code. If that's the case, here goes:

It looks like you never reach the end condition that you want. I mean, you get your run_started variable to get to True. But then what? Nothing else happens in your code since you never trigger the else statement that is contrary to your if t > 0 statement. I think you should re-think the logic in your code and what you want it to do exactly

EDIT: your code commented, I've put numbers in {{}} to show what happens in what order

list1 = [1, 2, 3, 4]
print (list1)
length = len(list1)
run_started = False
for x in range(length - 1): # Loop begins {{1}}
    t = list1[x + 1] - list1[x]
    if t > 0 : 
        if run_started:
            run_length = x #You keep setting a new run_length {{3}}
        else:
            run_started = True #You've set run_started  {{2}}
            run_length = x
    else:
        if run_started:
            print (True)
            print ("Run Length: {0}".format(run_length))
            break
if not run_started: #Then this is called after the loop {{4}} but it doesn't return true
    print (False)

As you can see, you exit the for loop without ever calling else on line 13. Furthermore then final if not run_started is also never called. Your code works the way it is designed to work, though maybe not the way you want it to work. HTH

Alex Chumbley
  • 3,062
  • 7
  • 31
  • 58
  • im trying to make it so it tells when it starts acending and for how long – user2980776 Nov 11 '13 at 21:53
  • Well, since your test data is a list that is always ascending, your code will never output anything of use. What you can do (if you want to keep the same test data), is provide an else statement to go along with your "if not run_started". In that else statement, you can print out run_length. That way, if the run "has started", you get output. – Alex Chumbley Nov 11 '13 at 22:02
  • where would i put that – user2980776 Nov 11 '13 at 22:04
  • You said you want it to tell you when it starts ascending and how long it's ascending right? Well, here's an option. You put print("Started ascending") after run_started=True. Then, you need to print out your run_length in every iteration of your for loop, so you can keep everything the same but add print ("Run Length: {0}".format(run_length)) in your if t > 0: if run_started: block. That way, no matter what happens, every iteration where the run has started, you print out run_length. – Alex Chumbley Nov 12 '13 at 00:15
0

A way that is more efficient than fully constructing the sorted list (and should work for arbitrary iterable types, containing any data types that allow for greater-than comparisons):

def len_ascending_run_from_start(seq):
    vals = enumerate(seq)
    try:
        c,x = 1, vals.next()[1]
    except StopIteration:
        raise ValueError("Sequence is empty!")
    for i, cur_x in vals:
        if cur_x < x:
            break
        c,x = c+1, cur_x
    return c

E.g.:

In [44]: len_ascending_run_from_start([datetime.date(2012,1,4), datetime.date(2012,1,3), datetime.date(2012,1,5)])
Out[44]: 1

In [45]: len_ascending_run_from_start([datetime.date(2012,1,4), datetime.date(2012,1,6), datetime.date(2012,1,5)])
Out[45]: 2

In [46]: len_ascending_run_from_start([datetime.date(2012,1,4), datetime.date(2012,1,6), datetime.date(2012,1,7)])
Out[46]: 3

In [47]: len_ascending_run_from_start((1,2,3,4))
Out[47]: 4

In [48]: len_ascending_run_from_start((1,3,2,4))
Out[48]: 2

In [49]: len_ascending_run_from_start(set([1,3,2,4]))
Out[49]: 4

It could also be fun / useful to make a class such that len automatically reports this statistic about the underlying sequence (and keeps a simplistic cache of the result, to avoid calculating it repeatedly).

class AscendingRunFromStart(object):
    def __init__(self, seq):
        self.seq = seq

    def __repr__(self):
        return self.seq.__repr__()
    __str__ = __repr__

    def __len__(self):
        if hasattr(self, "_len"):
            return self._len
        vals = enumerate(self.seq)
        c,x = 1, vals.next()[1]
        for i, cur_x in vals:
            if cur_x < x:
                break
            c,x = c+1, cur_x
        self._len = c
        return self._len

E.g.:

In [76]: x = AscendingRunFromStart([1,3,2,4])

In [77]: x
Out[77]: [1, 3, 2, 4]

In [78]: len(x)
Out[78]: 2

A good extension would be to use a Descriptor pattern to make the seq attribute of the class read-only, or to invoke an updated len calculation whenever set is called. (Left as exercise for the reader...)

ely
  • 70,012
  • 31
  • 140
  • 215
  • Although this is clearly faster (and doesn't require a copy) than the answer I posted, it has the drawback of preventing a custom comparison function to be given (one other than – jbat100 Nov 11 '13 at 22:36
  • The things you point out are correct and meaningless. Also, it's easy to override the behavior of ` – ely Nov 12 '13 at 18:20
  • Perhaps the most meaningless is the comment about how much "shorter" yours is. Optimizing lines of code can sometimes be fun and force clever or "Pythonic" implementations, but getting one-liners is usually useless from a pragmatic point of view. – ely Nov 12 '13 at 18:21
  • all i'm saying is i would use mine unless performance was an issue. It often isn't, and i don't think my answer is either terrible nor useless. Goodbye. – jbat100 Nov 12 '13 at 19:19
  • I'm sorry you feel that way, but I disagree. It also doesn't solve the OP's problem regardless (since knowing the length of the ascending run from the start is what's needed). If you're interested in solving a different problem than the one asked, and doing it in an inefficient way when an efficient way is super easy to code in just about no time at all, that's your perspective. But don't expect people not to criticize that. – ely Nov 12 '13 at 19:34
  • it's funny. It seems the community disagrees. – jbat100 Nov 12 '13 at 22:28
  • Good thing the fact that your answer fails to address the OP's question is a matter of incontrovertible fact, so that the 3 up-voters (representing nothing but noise given how small that number of up-votes is) are irrelevant. – ely Nov 13 '13 at 14:28
  • it does answer the question, it does not address the (extra) requirement added (after my answer) in the 5th comment. – jbat100 Nov 13 '13 at 15:09
  • Comments can clarify what a question's intention is. That is a thing comments can do. In response to a comment, deleting an answer that addressed a mistaken first draft of a question can be an appropriate thing to do. Editing an answer to answer the clarified version is also a thing. Answers that only address mistaken first versions of things, especially when said answer already exists on a different post that *did* *actually* ask the question from the first draft, seem especially good candidates for being willingly deleted by their authors, if said authors care about the community above rep. – ely Nov 13 '13 at 15:28