Work the Shell - Spreading Out Numbers

by Dave Taylor

在过去的几个月里,我们一直在编写一个电影问答游戏,目的是将其作为一个 Twitter 客户端,并在其 Twitter feed 上零星地吐出问题,形式为“电影《日落大道》是在 1943 年、1946 年还是 1950 年上映的?”

最初看起来最困难的任务是找到电影列表,然后提取上映日期,但通过利用出色的互联网电影数据库网站 (imdb.com) 并通过一些过滤器和转换推送数据,结果证明这是一个可以完成的任务。

最终结果是,通过简单地调用一个脚本,我们可以生成一个名为 top-250-films-with-release-dates.db 的数据文件,其内容如下:“日落大道 | 1950”(现在您知道第一段中问题的答案了)。

生成有趣的相邻数字

上期专栏留下的难题是生成良好的“相邻”上映年份。也就是说,如果我们谈论的是像 《纳尼亚王子卡斯宾》 这样的电影,它于 2008 年上映,我们希望相邻的值非常接近——可能是 2005 年和 2007 年。如果我们谈论的是 《后窗》,它早在 1954 年上映,我们希望相邻的值分布得更开,因为提供 1951 年、1954 年和 1955 年的版本会比 1940 年、1950 年和 1954 年或类似的版本更令人恼火和吹毛求疵。明白我的意思吗?

我们可以做的只是从当前年份中减去上映年份,然后应用某种倍数来调整增量。然后,《纳尼亚王子卡斯宾》 的“邻近度”为零,而 《后窗》 的邻近度为 54。让我们考虑将该值除以五,并使用上限值,看看计算半打电影会产生什么结果(表 1)。

表 1. 计算电影问答游戏的邻近度

标题上映日期邻近度因子
帝国的毁灭200441
大都会19278117
罪恶之城200531
唐人街1974347
热情似火19594910

还不错。《罪恶之城》 的错误年份值可能在实际发行年份的一年之内,而 《大都会》 的错误年份值可能相差 17 年,而大多数人都没有意识到。我的意思是,如果我现在问你,“弗里茨·朗的杰作 《大都会》 是在 1927 年、1931 年还是 1947 年上映的?”,你会知道答案吗?

这引出了一个重要的认识:我们无法让这些值完全均匀分布,因此上面的因子是 1..因子选择的上限范围。因此,有趣的 《热情似火》 的错误猜测可能与实际年份相差一年到九年不等。

好了,讨论够了。我们如何在代码中实现这一点?

好吧,我们在 releasedate 中有电影的上映日期,我们在 thisyear 中有当前年份,所以这是一个简单的测试脚本

thisyear="$(date +%Y)"
releasedate="$1"
adjacency="$(( $thisyear - $releasedate ))"
if [ $adjacency -lt 5 ] ; then
  factor="1"
else
  factor="$(( $adjacency / 5 + 1 ))"
fi
echo "For release $releasedate we have factor = $factor"

这展示了 shell 脚本编写的一个重要方面:有时思考解决方案比实际编写结果算法更耗时。我可以分享一个关于我的老板告诉我在我早期的工作中“停止思考并开始编码”的轶事,但我会跳过它。请记住,思考解决方案路径是任何工作中至关重要的一步。

现在我们有了一种计算给定电影上映年份的邻近度因子的方法,让我们迈出下一步,实际计算可能的值

delta="$(( $RANDOM % $factor + 1))"
add="$(( $RANDOM % 2 ))"
if [ $add -eq 1 ] ; then
  closeyear="$(( $releasedate + $delta ))"
else
  closeyear="$(( $releasedate - $delta ))"
fi

作为第一步,这还不错。

然而,我看到这个算法目前存在两个问题。首先,我们最终可能会得到未来的上映年份(也就是说,《钢铁侠》 最终可能会得到 2009 年的上映年份,这是错误的)。其次,对于过去五年上映的电影,在删除重复项后,我们也可能最终得到始终夹在中间的实际发行年份。(我希望你能明白为什么会这样。)

为了解决第一个问题,我们需要添加一个测试,以确保 closeyear 永远不大于 thisyear,这很简单。对于第二个问题,我认为最小增量为 2 而不是 1 可以为我们提供更多的回旋余地,尽管对于那些即使给予最少关注的人来说,在当年上映的任何电影基本上都是白给的。

以下是我如何实现这些调整的

if [ $adjacency -lt 5 ] ; then
  factor="2"
else
  factor="$(( $adjacency / 5 + 1 ))"
fi

并且,在代码中稍后

if [ $closeyear -gt $thisyear ] ; then
  closeyear="$(( $releasedate - $delta ))"
fi

这似乎效果很好。现在当我们给脚本几个不同的上映年份时,这就是我们看到的

Release Year    First Five Generated Results
1962            1970, 1967, 1958, 1960, 1971
1994            1996, 1996, 1995, 1993, 1993
2002            2004, 2001, 2000, 2001, 2003
1927            1915, 1925, 1937, 1936, 1911
2008            2006, 2007, 2007, 2006, 2007

我认为我们可以接受这个——实际上还不错。

现在我们有了所有的构建块,下个月,我们将把它们放在一起,创建电影问答游戏。运气好的话,我们将有空间开始在 Twitter 上推送它。与此同时,如果您想在 Twitter 上注册该游戏并观看我的开发过程,请关注 FilmBuzz。

Dave Taylor 是 UNIX 领域的 26 年资深人士,Elm Mail System 的创建者,以及最近畅销书 《Wicked Cool Shell Scripts》《Teach Yourself Unix in 24 Hours》 的作者,以及他的 16 本技术书籍。他的主要网站是 www.intuitive.com,他还提供技术支持,网址为 AskDaveTaylor.com。如果您愿意,请在 Twitter 上关注他:twitter.com/DaveTaylor

加载 Disqus 评论