7
\$\begingroup\$

Problem Statement

The professor is conducting a course on Discrete Mathematics to a class of N students. He is angry at the lack of their discipline, and he decides to cancel the class if there are fewer than K students present after the class starts.

Given the arrival time of each student, your task is to find out if the class gets cancelled or not.

Input Format

The first line of the input contains T, the number of test cases. Each test case contains two lines. The first line of each test case contains two space-separated integers, N and K. The next line contains N space-separated integers, \$a_1,a_2,…,a_N\$, representing the arrival time of each student.

If the arrival time of a given student is a non-positive integer (\$a_i≤0\$), then the student enters before the class starts. If the arrival time of a given student is a positive integer (\$a_i>0\$), the student enters after the class has started.

Output Format

For each testcase, print "YES" (without quotes) if the class gets cancelled and "NO" (without quotes) otherwise.

Constraints

$$\begin{align} 1&≤T≤10,\\ 1&≤N≤1000,\\ 1&≤K≤N,\\ −100&≤a_i≤100, \text{where}\ i∈[1,N] \end{align}$$

Note

If a student enters the class exactly when it starts (\$a_i=0\$), the student is considered to have entered before the class has started.

Sample Input

2
4 3
-1 -3 4 2
4 2
0 -1 2 1

Sample Output

YES
NO

Solution

def is_on_time(a):
    return a <= 0

def class_cancelled(expected, arrival_times):
    actual = map(lambda x: 1 if is_on_time(x) else 0, arrival_times).count(1)
    return actual >= expected

def read_int():
    return int(raw_input())

def read_ints():
    return map(int, raw_input().strip().split())

if __name__ == '__main__':
    T = read_int()
    for _ in xrange(T):
        N, K = read_ints()
        A = read_ints()
        print "NO" if class_cancelled(K, A) else "YES"
\$\endgroup\$
7
\$\begingroup\$
def class_cancelled(expected, arrival_times):
    ....
    return actual >= expected

print "NO" if class_cancelled(K, A) else "YES"

class_cancelled is a misnomer. It in fact computes a class_not_cancelled condition: class should be cancelled when actual < expected. Also the problem asks to print "Yes" when the class gets cancelled. Funny how two wrongs make it right.

\$\endgroup\$
2
\$\begingroup\$

The roll call can be accomplished using sum(), which is the simplest way to count True values:

def on_time_attendees(arrival_times):
    return sum(t <= 0 for t in arrival_times)

As @vnp mentioned, class_cancelled() actually does the opposite of what its name suggests. Now that you have introduced that confusion, the best way out may be to eliminate that function entirely.

for test_case in xrange(read_int()):
    n, k = read_ints()
    print("YES" if on_time_attendees(read_ints()) < k else "NO")
\$\endgroup\$
1
\$\begingroup\$

Readability Counts

I challenge you to come back in a week and tell me what this code does:

def class_cancelled(expected, arrival_times):
    actual = map(lambda x: 1 if is_on_time(x) else 0, arrival_times).count(1)
    return actual >= expected

First of all, you could rewrite it without the lambda by doing:

actual = map(is_on_time, arrival_times).count(True)

but, while more terse, that doesn't actually aid reability either. You could drop the count() by instead using filter():

actual = len(filter(is_on_time, arrival_times))

but that's not really any better either.

Both are also inefficient. We're constructing a new list based on arrival_times, and then iterating over it again to count the 1s or Trues instead of counting them as we go. We can use a generator expression for that:

def class_cancelled(expected, arrival_times):
    actual = sum(1 for t in arrival_times
                 if is_on_time(t))
    return actual >= expected

Or, even better:

def number_on_time(arrival_times):
    return sum(1 for t in arrival_times
               if t <= 0)

...
print "NO" if number_on_time(A) >= K else "YES"

I wouldn't put the non-positive check in its own function. This is clear enough as it is.


If you want to refactor anything, I would add something like this to your toolkit:

def count_if(iterable, pred):
    return sum(1 for x in iterable if pred(x))

So that:

def number_on_time(arrival_times):
    return count_if(arrival_times, lambda t: t <= 0)

That is easily understood.

\$\endgroup\$
  • \$\begingroup\$ Could you please elaborate a little on why you consider that len(filter(is_on_time, arrival_times)) is such unreadable when compared sum(1 for t in arrival_times if t <= 0)? \$\endgroup\$ – Borsunho Oct 5 '15 at 20:10
  • \$\begingroup\$ Because I find your answer quite misleading. Order of reading that happens to be similar to English sentence expressing the idea doesn't make this operation any more readable. To understand what sum is for reader needs to read comprehension first, just like with functional version one needs to read filter call before he understands what len really does. One could agree that later version is more 'visually pleasing', 'idiomatic' or 'pythonic', but readability as in 'ease of understanding' is pretty much the same. \$\endgroup\$ – Borsunho Oct 5 '15 at 21:21
  • \$\begingroup\$ @Borsunho I don't know how you group 'visually pleasing', 'idiomatic', and 'pythonic' as somehow separate categories from 'ease of understanding'. The fact that you believe it fits the first three pretty much definitely states that it fits the latter one. But YMMV. \$\endgroup\$ – Barry Oct 5 '15 at 21:23

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.