Monthly Archives: April 2013

Review: CS102: Introduction to Computer Science II — Saylor Foundation

After covering basic concepts of programming and computer science in CS101, Saylor’s CS102: Introduction to Computer Science II moves on to discuss some more advanced material, using the C family of languages, i.e. C, C++ and Java.

I did appreciate the many historic sources I was exposed to, such as Dennis Ritchie’s article “The Development of the C Language”. I do think that in computer science textbooks there is often very little appreciation of the rich history of this field. This is a pity, since learning about the evolution of programming languages can only broaden your horizon.

The other aspect I enjoyed was the strong comparative focus. Some assignments consisted of implementing a simple program in both Java and C++, and asking to the student to pay careful attention to similarities and differences. This reminded me of CS101, which occasionally referred to both Python and Java. Overall, this strikes me as an excellent idea. Would more students be exposed to different languages early on, even if it only happened on a superficial level, you probably wouldn’t see so many insecure people online who defend Java because it’s the only language they have ever been exposed to. This isn’t necessarily a jab at Java, but at the educational system. Had they learnt Python, they would instead pollute Reddit and other places with remarks that Python is all you’ll ever need.

CS102 was mostly about concepts. You could learn about object-oriented programming and its history, tip your toes into functional programming by going through introductory examples of recursion, and familiarize yourself with the Java collections framework as well as the C++ STL on a basic level. It was all intended as a first exposure to the topics, presumably to make subsequent courses more accessible.

The teaching materials were mostly of a high quality. David Eck’s free online book, Introduction to Programming Using Java, featured prominently in the course. It is very well written. The author makes sure to explain each concept and gradually build upon it. I have seen quite a few introductory books that were full of hand-wavy explanations, or appeals to the reader to just accept something at face value for the time being. In addition, there were a few lectures taken from Stanford’s CS106B: Programming Abstractions, some from MIT’s 6.00: Introduction to Computer Science and Programming and some videos on sorting and recursion from Khan Academy. Those were sources I was familiar with already. I did not know about the accessible illustrations of sorting algorithms by Xoax.net, though. For a beginner, they may well be the most suitable resource online. I wish I had seen those videos when I first studied sorting algorithms.

I had very few gripes with the course. First, while the materials were generally very good, one (external) assessment quiz was so bad that it made me laugh. Here is a screenshot of the quick sort quiz:

Amateurish quiz

So, what are the problems with it? The presentation is poor, and so is the content. The first question is ill-defined since quick sort requires picking a pivot element first. Amusingly enough, the second question asks about this. Lastly, the definition of “divide and conquer” is far too sloppy. It should not take many resources for Saylor to drop this quiz and instead create one themselves.

Second, some sources were too specific and technical. In particular, one introduction to the STL and generic programming, in Unit 7.1, was addressing experienced programmers and was therefore inappropriate for this course. Third, even though CS102 covered a relatively wide spectrum, the final exam mostly focussed on definitions related to OOP and language particulars of Java and C++. Thus, this experience was anticlimactic. I would have expected at least some coverage of sorting algorithms, or a question or two on the analysis of algorithms. Further, a few of the questions were downright pedantic, asking for minutiae like the exact names of specific Java object methods. With a bit of programming experience, they are trivial answer, which made me think of Feynman’s statement that there is a difference between knowing the name of something and knowing something. I would have appreciated a stronger focus on conceptual understanding.

Before I enrolled in this course, I peeked ahead, and learnt that CS107: C++ Programming contains substantial programming assignments. Thus, I viewed CS102 as a course that introduces concepts in preparation for follow-up courses. Therefore, I don’t view the lack of more challenging programming exercises as negative. Overall, I think that this course is well-designed. In fact, the approach Saylor pursues by laying a strong theoretical foundation first before properly introducing students to programming may be superior to what you normally experience, i.e. either mostly ignoring theory and history and merely focussing on practical knowledge and risking that students won’t acquire a solid foundation since they don’t really know what they are doing, or trying to teach programming and CS concepts at the same time, which can be confusing for students.

Coding Bat: Python. List-2

All solutions were successfully tested on 18 April 2013.

count_evens:

def count_evens(nums):
  count = 0
  for element in nums:
    if element % 2 == 0:
      count += 1
  return count

big_diff:

def big_diff(nums):
  return max(nums) - min(nums)

centered_average:

def centered_average(nums):
  sum = 0
  for element in nums:
    sum += element
  return (sum - min(nums) - max(nums)) / (len(nums)-2) 

sum13:

def sum13(nums):
  if len(nums) == 0:
    return 0

  for i in range(0, len(nums)):
    if nums[i] == 13:
      nums[i] = 0
      if i+1 < len(nums): 
        nums[i+1] = 0
  return sum(nums)

sum67:

def sum67(nums):
  for i in range(0, len(nums)):
    if nums[i] == 6:
      nums[i] = 0
      for j in range(i+1, len(nums)):
        temp = nums[j]
        nums[j] = 0
        if temp == 7:
          i = j + 1
          break
  return sum(nums)

Line 9 is not necessary. However, by adjusting “i” you ensure that this script runs in linear time, despite the nested loop.

has22:

def has22(nums):
  for i in range(0, len(nums)-1):
    #if nums[i] == 2 and nums[i+1] == 2:
    if nums[i:i+2] == [2,2]:
      return True     
  return False

The second option is much nicer to look at, but either way is fine.

Coding Bat: Python. String-2

All solutions were successfully tested on 18 April 2013.

double_char:

def double_char(str):
  result = ''
  for char in str:
    result += char * 2
  return result

count_hi:

def count_hi(str):
  count = 0
  for i in range(len(str)-1):
    if str[i:i+2] == 'hi':
      count += 1
  return count

cat_dog:

def cat_dog(str):
  count_cat = 0
  count_dog = 0
  for i in range(len(str)-2):
    if str[i:i+3] == 'dog':
      count_dog += 1
    if str[i:i+3] == 'cat':
      count_cat += 1
  
  return count_cat == count_dog

count_code:

def count_code(str):
  count = 0
  for i in range(0, len(str)-3):
    if str[i:i+2] == 'co' and str[i+3] == 'e':
      count += 1
  return count

end_other:

def end_other(a, b):
  a = a.lower()
  b = b.lower()
  #return (b.endswith(a) or a.endswith(b))
  return a[-(len(b)):] == b or a == b[-(len(a)):] 

Either way is fine.

xyz_there:

def xyz_there(str):
  for i in range(len(str)):
    if str[i] != '.' and str[i+1:i+4] == 'xyz':
      return True
  if str[0:3] == 'xyz':
    return True
  return False

Coding Bat: Python. Logic-2

All solutions were successfully tested on 18 April 2013.

make_bricks:

def make_bricks(small, big, goal):
  return goal%5 >= 0 and goal%5 - small <= 0 and small + 5*big >= goal

lone_sum:

def lone_sum(a, b, c):
  if a == b == c:
    return 0
  if b == c:
    return a
  if a == c:
    return b
  if a == b:
    return c  
  return a + b + c

lucky_sum:

def lucky_sum(a, b, c):
  if a == 13:
    return 0
  if b == 13:
    return a
  if c == 13:
    return a + b
  return a + b + c

no_teen_sum:

def no_teen_sum(a, b, c):
  return fix_teen(a) + fix_teen(b) + fix_teen(c)

def fix_teen(n):
  #if 13 <= n <= 14 or 17 <= n <= 19:
  if n in [13, 14, 17, 18, 19]:
    return 0
  return n

I consider checking for list membership to be more elegant than multiple comparison operations.

round_sum:

def round_sum(a, b, c):
  return round10(a) + round10(b) + round10(c)

def round10(n):
  if n % 10 >= 5:
    return n + 10 - (n % 10)
  return n - (n % 10)  

close_far:

def close_far(a, b, c):
  cond1 = abs(a-b) <= 1 and abs(b-c) >=2 and abs(a-c) >= 2
  cond2 = abs(a-c) <= 1 and abs(a-b) >=2 and abs(c-b) >= 2
  return cond1 or cond2

make_chocolate:

def make_chocolate(small, big, goal):
  maxBig = goal / 5
  
  if big >= maxBig:
    if small >= (goal - maxBig * 5):
      return goal - maxBig * 5
  if big < maxBig:
    if small >= (goal - big * 5):
      return goal - big * 5
  return -1

Coding Bat: Python. Logic-1

All solutions were successfully tested on 17 April 2013.

cigar_party:

def cigar_party(cigars, is_weekend):
  if is_weekend:
    return cigars >= 40
  return 40 <= cigars <= 60

Pay attention to the last line! In Python it is possible to concatenate comparisons, just like you would do it in mathematics. This can lead to much cleaner code. In my opinion, the solution from the website is worse, but not just for that reason alone:

def cigar_party(cigars, is_weekend):
  if is_weekend:
    return (cigars >= 40)
  else:
    return (cigars >= 40 and cigars <= 60)

date_fashion:

def date_fashion(you, date):
  if you <= 2 or date <= 2:
    return 0
  if you >= 8 or date >= 8:
    return 2
  return 1

squirrel_play:

def squirrel_play(temp, is_summer):
  if is_summer:
    return 60 <= temp <= 100
  return 60 <= temp <= 90

caught_speeding:

def caught_speeding(speed, is_birthday):
  if is_birthday:
    speed -= 5
    
  if speed <= 60:
      return 0
  if 60 < speed <= 80:
    return 1
  return 2

sorta_sum:

def sorta_sum(a, b):
  if 10 <= a + b < 20:
    return 20
  return a + b

It is not necessary to put “a + b” in line 2 inside parentheses due to the rules of precedence of operators. A less experienced human reader might be able to parse this line more quickly with parens, though. However, you shouldn’t assume that you write code for a complete beginner.

alarm_clock:

def alarm_clock(day, vacation):
  if not vacation:
    if 1 <= day <= 5:
      return '7:00'
    return '10:00'
 
  if 1 <= day <= 5:
    return '10:00' 
  return 'off'

love6:

def love6(a, b):
  return a == 6 or b == 6 or (a + b) == 6 or abs(a - b) == 6

What, this looks ugly you say? I completely agree, and there is a much more pleasant solution:

def love6(a, b):
  return 6 in [a, b, a + b, abs(a - b)]

in1to10:

def in1to10(n, outside_mode):
  if not outside_mode:
    return n in range(1, 11)
  return n <= 1 or n >= 10

near_ten:

def near_ten(num):
 # return 0 <= (num % 10) <= 2 or 8 <= (num % 10) <= 10
 return num % 10 in [0,1,2,8,9,10]

Again, do you go for ugly or nice and clean?