一流のレストラン


ドラマ 『Chef~三ツ星の給食~』(天海祐希)
ネタバレ注意です。三ツ星レストランのシェフが、レストランをクビになり、小学校の給食調理員になります。自分が納得する料理を作るため、さんざん子どもたちを待たせて、出した料理は、子どもたちの口にはまったく合いませんでした。そこまで不味いっていうのかあ。。。まあ味覚の違いは人それぞれ。大人と子供だけでなく、何を食べて生きてきたかでも味覚って異なりますよね。不味くて食べられたものじゃない味でも、人によっては至高の味だったり。納豆とかチーズとか、まさに好き嫌いあるかなあと思います。私も子供の頃は、イナゴを好んで食べてましたし。今は、目が合うと可哀想に思えるので、食べるのは無理ですが。
ソフトウェアの世界でも、ある人から見ると最高のソースコードでも、別の人から見ると下手過ぎてどうしようもないソースコードだったりすることがあるようです。先日、NetBSDとGNUのソースコードを、誰が作ったのかは伏せて隣人ベテラン技術者に見せたら、「こんな下手なソースコードよく書けるなあ。新人が書いたの?」と言ってました。高級ワインを格安ワインと偽って飲ませて、文句を浴びせられた感じでしょうか。美味しいと言って欲しい!

今回は、NetBSDとGNUの、同じ関数ではあるが、異なるソースコードを見ていきます。
まずはNetBSD。ソース取得のためCVSインストールから。

$ su -
# apt-get install cvs

(省略)

# export CVSROOT=:pserver:anoncvs@anoncvs.NetBSD.org:/cvsroot
# cvs login

anoncvsと入力します。

そしてチェックアウト。

# netbsdsrc$cvs co src

チェックアウトは時間がかかりました。

今回は、strcmp関数を比較対象にします。

root@deb83:/usr/src/netbsd# find -name strcmp.c
./src/common/lib/libc/string/strcmp.c
root@deb83:/usr/src/netbsd# 

ついでに行数を確認します。

root@deb83:/usr/src/netbsd# !! | xargs wc -l
find -name strcmp.c | xargs wc -l
67 ./src/common/lib/libc/string/strcmp.c
root@deb83:/usr/src/netbsd# 

全文見てみましょう。

root@deb83:/usr/src/netbsd# !-2 | xargs cat -n
     1	/*	$NetBSD: strcmp.c,v 1.3 2013/07/01 20:51:59 joerg Exp $	*/
     2	
     3	/*-
     4	 * Copyright (c) 1990, 1993
     5	 *	The Regents of the University of California.  All rights reserved.
     6	 *
     7	 * This code is derived from software contributed to Berkeley by
     8	 * Chris Torek.
     9	 *
    10	 * Redistribution and use in source and binary forms, with or without
    11	 * modification, are permitted provided that the following conditions
    12	 * are met:
    13	 * 1. Redistributions of source code must retain the above copyright
    14	 *    notice, this list of conditions and the following disclaimer.
    15	 * 2. Redistributions in binary form must reproduce the above copyright
    16	 *    notice, this list of conditions and the following disclaimer in the
    17	 *    documentation and/or other materials provided with the distribution.
    18	 * 3. Neither the name of the University nor the names of its contributors
    19	 *    may be used to endorse or promote products derived from this software
    20	 *    without specific prior written permission.
    21	 *
    22	 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
    23	 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    24	 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    25	 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
    26	 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
    27	 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
    28	 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
    29	 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
    30	 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
    31	 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
    32	 * SUCH DAMAGE.
    33	 */
    34	
    35	#include <sys/cdefs.h>
    36	#if defined(LIBC_SCCS) && !defined(lint)
    37	#if 0
    38	static char sccsid[] = "@(#)strcmp.c	8.1 (Berkeley) 6/4/93";
    39	#else
    40	__RCSID("$NetBSD: strcmp.c,v 1.3 2013/07/01 20:51:59 joerg Exp $");
    41	#endif
    42	#endif /* LIBC_SCCS and not lint */
    43	
    44	#if !defined(_KERNEL) && !defined(_STANDALONE)
    45	#include <assert.h>
    46	#include <string.h>
    47	#else
    48	#include <lib/libkern/libkern.h>
    49	#endif
    50	
    51	#undef strcmp
    52	
    53	/*
    54	 * Compare strings.
    55	 */
    56	int
    57	strcmp(const char *s1, const char *s2)
    58	{
    59	
    60		_DIAGASSERT(s1 != NULL);
    61		_DIAGASSERT(s2 != NULL);
    62	
    63		while (*s1 == *s2++)
    64			if (*s1++ == 0)
    65				return (0);
    66		return (*(const unsigned char *)s1 - *(const unsigned char *)--s2);
    67	}

strcmpの処理は56行目からですね。面白いのは、63行目のs1とs2のポインタの進め方が異なるという点です。文字列を1文字ずつ順に比較していき、NULLまで一致していることが確認できたら0を返します。それ以外は、不一致の文字コードの差分を戻り値にしています。
この辺りは、manの説明ではこのようにあります。

STRCMP(3)                                    Linux Programmer's Manual                                    STRCMP(3)



名前
       strcmp, strncmp - 二つの文字列を比べる

書式
       #include <string.h>

       int strcmp(const char *s1, const char *s2);

       int strncmp(const char *s1, const char *s2, size_t n);

説明
       strcmp()  関数は二つの文字列 s1 と s2 を較べる。 この関数は、 s1 が s2 に較べて 1)小さい、2)等しい、3)大きい
       場合に、 ゼロよりも 1)小さい、2)等しい、3)大きい整数を返す。

       strncmp() 関数は、s1 と s2 の最初の n バイトだけを比較する ことを除けば、strcmp()と同様である。

返り値
       strcmp()  関数と strncmp()  関数は整数を返す。  この整数は、ゼロよりも、1)小さい、2)等しい、3)大きいのいずれ
       かである。 それぞれは、s1(または、この文字列の最初の n バイト)が s2 よりも、1)小さい、2)等しい、3)大きいに
       対応している。

次は、GNU版を見てみます。同じファイル名を探して、癖のように行数もチェック。

root@deb83:/usr/src/glibc# find -name strcmp.c
./string/strcmp.c
root@deb83:/usr/src/glibc# !!|xargs wc -l
find -name strcmp.c|xargs wc -l
46 ./string/strcmp.c

では全文表示しましょう。

root@deb83:/usr/src/glibc# !-2|xargs cat -n
     1	/* Copyright (C) 1991-2014 Free Software Foundation, Inc.
     2	   This file is part of the GNU C Library.
     3	
     4	   The GNU C Library is free software; you can redistribute it and/or
     5	   modify it under the terms of the GNU Lesser General Public
     6	   License as published by the Free Software Foundation; either
     7	   version 2.1 of the License, or (at your option) any later version.
     8	
     9	   The GNU C Library is distributed in the hope that it will be useful,
    10	   but WITHOUT ANY WARRANTY; without even the implied warranty of
    11	   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    12	   Lesser General Public License for more details.
    13	
    14	   You should have received a copy of the GNU Lesser General Public
    15	   License along with the GNU C Library; if not, see
    16	   <http://www.gnu.org/licenses/>.  */
    17	
    18	#include <string.h>
    19	#include <memcopy.h>
    20	
    21	#undef strcmp
    22	
    23	/* Compare S1 and S2, returning less than, equal to or
    24	   greater than zero if S1 is lexicographically less than,
    25	   equal to or greater than S2.  */
    26	int
    27	strcmp (p1, p2)
    28	     const char *p1;
    29	     const char *p2;
    30	{
    31	  const unsigned char *s1 = (const unsigned char *) p1;
    32	  const unsigned char *s2 = (const unsigned char *) p2;
    33	  unsigned char c1, c2;
    34	
    35	  do
    36	    {
    37	      c1 = (unsigned char) *s1++;
    38	      c2 = (unsigned char) *s2++;
    39	      if (c1 == '\0')
    40		return c1 - c2;
    41	    }
    42	  while (c1 == c2);
    43	
    44	  return c1 - c2;
    45	}
    46	libc_hidden_builtin_def (strcmp)

こちらは、関数の引数をそのまま使わないのですね。比較の一つ一つは、文字列ではなくキャラの比較だと分かるようにc1,c2という変数に入れ直しています。判定に関しては、NetBSDと逆、つまり、一致判定よりもNULLチェックの方が先になっています。判定は、40行目の計算式におまかせする形です。
同じ関数なのに、随分考え方が異なっていますね。しかし、どちらも洗練されていて、読むのがわくわくするソースコードだと思います。

凡人な私の料理だとどうなるでしょう。自作してみます。

     1	#include <stdio.h>
     2	
     3	int my_strcmp(const char *s1, const char *s2)
     4	{
     5	        while(*s1 == *s2)
     6	                if(*s1 == 0)
     7	                        break;
     8	                else{
     9	                        s1++;
    10	                        s2++;
    11	                }
    12	
    13	        return (int)*s1 - (int)*s2;
    14	}
    15	
    16	#define S1 argv[1]
    17	#define S2 argv[2]
    18	
    19	int main(int argc, char* argv[])
    20	{
    21	        printf("S1=%s S2=%s : return=%d\n",S1, S2, my_strcmp(S1,S2));
    22	
    23	        return 0;
    24	}

それぞれのプログラマーをシェフだとすると、NetBSDもGNUも一流のレストランであることには違いありません。一流だからこそ、こだわりが出てくるんだと思います。

コメント

タイトルとURLをコピーしました