h-index #3

Haskell は分からない、という、はてブ指数を言い出した人がブックマークでコメントしていたので、同等の処理を RubyPython で。
余計なことにユニットテスト付き。

module Enumerable
   def take_while
      ret = []
      each do |val|
         break unless yield val
         ret << val
      end
      return ret
   end
end

def hindex(array)
   len = array.size
   (1..len).zip(array.sort.reverse).take_while do |i, val|
      val >= i
   end.size
end

if $0 == __FILE__
   require 'test/unit'

   class HIndexTest < Test::Unit::TestCase
      def test_hindex
         assert_hindex 4, [0, 10, 20, 30, 40]
         assert_hindex 4, [10, 20, 30, 40]
         assert_hindex 3, [20, 30, 40]
         assert_hindex 2, [30, 40]
         assert_hindex 1, [40]
         assert_hindex 2, [1, 2, 3]
         assert_hindex 3, [3, 3, 3]
         assert_hindex 2, [3, 3]
         assert_hindex 1, [3]
         assert_hindex 0, [0, 0]
         assert_hindex 0, [0]
         assert_hindex 0, []
      end

      def assert_hindex(expected, input)
         assert_equal(expected, hindex(input))
      end
   end
end

理屈の上では take_while ではなく、select でもいい。

from itertools import count, izip, takewhile

def iterlen(iterable):
    l = 0
    for i in iterable:
        l += 1
    return l

def hindex(lst):
    """
    >>> hindex([0, 10, 20, 30, 40])
    4
    >>> hindex([10, 20, 30, 40])
    4
    >>> hindex([20, 30, 40])
    3
    >>> hindex([30, 40])
    2
    >>> hindex([40])
    1
    >>> hindex([1, 2, 3])
    2
    >>> hindex([3, 3, 3])
    3
    >>> hindex([3, 3])
    2
    >>> hindex([3])
    1
    >>> hindex([0, 0])
    0
    >>> hindex([0])
    0
    >>> hindex([])
    0
    """
    def pred((rank, val)):
        return val >= rank

    return iterlen(takewhile(pred, izip(count(1), reversed(sorted(lst)))))

def _test():
    import doctest
    doctest.testmod()

if __name__ == '__main__':
    _test()

両者ともテストのコードが一番でかい。