做 CodeWars 一道题目的感想

感想

最近刷了几天 CodeWars, 感想颇多。CodeWars 更多的关注的是编程基础和语言特性,算法的比重小一点,测试用例也不怎么有边界情况,所以刷起来比较轻松有意思。刷了几天,感觉自己的编程水平又下降了。

感想如下:

  • 一是见识到了很多从来都没用过而且大概率从来不会去用的库函数,比如 str.center()

  • 二是见识到了那些疯狂写 one-liner 的 python 程序员。一个函数一定能用一行实现,如果不能,就两行。执行效率?不存在的。

  • 三是我 haskell 白学了,看到题还是不知道咋写。练习不够,没熟悉函数式编程的套路,罢了罢了。

一道题目

写这篇博客的主要原因是被一道题目的 one-liner 深深折服。

Snail

题目很简单,就是顺时针遍历二维数组,大部分人在初学 C++ 的时候肯定都写过吧。但是如果结合语言特性,就会衍生出神奇的解法。

我的低幼解法

我的解法很弱智,就是四个方向分别遍历,判断下边界。

def snail(array):
    x, y = 0, 0
    row, col = len(array), len(array[0])
    l = row * col if row == col else 0
    vis = array.copy()
    res = []
    drct = ((0,1),(1,0),(0,-1),(-1,0))
    d = 0
    for i in range(l):
        res.append(array[x][y])
        vis[x][y] = 'X'
        if (    x + drct[d][0] < 0 or x + drct[d][0] >= row or
                y + y + drct[d][1] < 0 or y + drct[d][1] >= col or
                vis[x + drct[d][0]][y + drct[d][1]] == 'X'):
            d = (d + 1) % 4
        x += drct[d][0]
        y += drct[d][1]
    return res

比较正常的解法

这个思路很清晰,就是预先算好每次遍历某行/列时走过的数字个数,然后也是四个方向轮转,比我写的好看很多。(主要是我懒得计算了)

def _snail(array):
    n = len(array)
    for a in range(0, (n + 1) // 2):
        b = n - 1 - a
        for j in range(a, b + 1):
            yield array[a][j]
        for i in range(a + 1, b + 1):
            yield array[i][b]
        for j in range(b - 1, a - 1, -1):
            yield array[b][j]
        for i in range(b - 1, a, -1):
            yield array[i][a]

def snail(array):
    return list(_snail(array)) if array and array[0] else []

摘抄自 用户 biskinis

one-liner 递归解法

这个是 CodeWars 里面投出的最聪明的做法

这种方法我是真的想不到…评论下面也是一片叫好声

def snail(array):
    return list(array[0]) + snail(zip(*array[1:])[::-1]) if array else []

解释: 就是每次取出一行,将剩下的数组巧妙地用 zip 函数逆时针翻转,然后继续递归求解,直到数组为空。

顺便一说,如果不强求 one-liner, 递归改成循环会更容易看懂。

最后

哇,我这篇博客写的真是太水了……

膜拜大佬们…