質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.47%
C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

Q&A

解決済

6回答

3347閲覧

プログラミング作法 問題1-2(新装版の場合21p)の解答について

退会済みユーザー

退会済みユーザー

総合スコア0

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

2グッド

3クリップ

投稿2018/02/01 09:34

編集2018/02/11 03:26

2018/2/11
区切りの為に書きます
この投稿にもう私が投稿するころはありません
皆さん質問ありがとうございました
回答者の方々どうしで議論するために使ってください

よろしければこちらのアイデアについて
ご意見がうかがえると嬉しいです
https://togetter.com/li/1198124

学業を収めることが卒業するために必要なんだから
趣味よりもそちらを最優先したほうがいいのではないかと思いました。
なので、こちらでの質問の返信は一時停止します。
せっかく答えてくださったのに、すぐに返信できずすみません

前提・実現したいこと

プログラミング作法
問題1-2(21p、新版)の解法

発生している問題・エラーメッセージ

問題の解答がこれで正しいのか自信がないです。

該当のソースコード

次の関数を改良せよという問題です

c

10#include <string.h> 21 int smaller(char *s, char *t){ 32 if (strcmp(s, t) < 1) 43 return 1; 54 else 65 return 0;}

試したこと・解答例

この問題は関数名と実装を適切に作成しようという内容の章で出題された問題です
関数名がsmallerでstrcmpを使用しているから数字の文字列二つ(例("12345","12345678"))を引数にとって、1番目の引数が2番目の引数より小さいかを比較する関数であることがわかります。
http://hitorilife.com/strcmp.php
によれば

int strcmp( const char *str1 , const char *str2 ); の場合

■戻り値:
str1とstr2が等しいならば0、
str1>str2ならば正の値、
str1<str2ならば負の値を返す。

なので前述のコードだとstr1とstr2が等しい場合にも1が返ってくる
"smaller than"は「より小さい」と訳す、これは「未満」とも言い換えられるが、比較対象と自分自身が等しい場合も含める「以下」とは訳せない。
よって二つの引数が等しい場合に0を返すようにに2行目だけ次のように書き換える。

c

1 if (strcmp(s, t) < 0)

補足情報

cのif文の条件分岐では数値の0以外が真になるので1が真、0が偽であることを自明として扱ったが

c

1#define TRUE 1 2#define FALSE 0

みたいな文を付け加えて真偽を明示すべきかということと

関数名smallerの名前の変え方、「引数1番目は引数2番目より短いか」と「引数2番目は引数1番目より短いか」の二重の解釈ができるが、これは引数1番目を主語とするのが普通(?)で、文脈も読み取った解釈だと普通(?)は前者を選択するからsmallerのままでいいのではないかと思えて、自分はあまりこの関数の名前を修正する必要を感じない

それともそのことを明示した関数名にしたほうがいいか。
例えば
「最初の変数が次の変数より小さいか」をgoogle翻訳した
「Whether the first variable is smaller than the next variable」
を圧縮したような変数名すべきか。
その場合どのような変数名にしたらわかりやすく短くできるか

それ以外に自分の気が付いてない改善点や解答にミスがないか

初投稿で勢いで投稿しているところがあるので
おかしいところがあったら指摘してください

LouiS0616, Yuno_556👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答6

0

関数名と実装を適切に作成しよう

やり取りの中で、次の仕様が明らかでないことが分かったかと思います。

  • どちらの値が小さいときに1を返す?
  • 『小さい』とはどんな尺度で?

smallerの命名を変えることによって、これらの曖昧さを除去する必要があります。

######どちらの値が小さいときに1を返す?
真面目に命名しようとすると、first_arg_is_smallerのように煩雑な命名になります。
既にstrcmpという前例があるのですから、これに倣えば良いでしょう。

ただし、何度も悩んでしまうのも良くないので、適切にコメントを付けると良いかと思います。

######『小さい』とはどんな尺度で?
これに関しましては、名前に意味を含めた方が良いかと思います。
なぜなら、今後の拡張によっては、その複数を実装する場合も考えられるからです。

辞書順の場合、???_in_lexical_order (これはただのstrcmpと同じ)
数値順の場合、???_in_numerical_orderなどが考えられるでしょう。

訂正:strcmp = 辞書順 は誤り。詳しくはa_saitohさんの回答をご覧ください。


すごく頭でっかちですけど、ここまで書けばかなり誤解は減るでしょう。

C

1/** 2@brief 数字列を数値に変換し、数値の大小を比較する。 3 4@param (s1) 数値に変換できる文字列。変換できない可能性は考慮しない。 5@param (s2) 数値に変換できる文字列。変換できない可能性は考慮しない。 6 7@return 次の凡例に従って戻り値を返す。 8 - strcmp_in_numerical_order("123", "12" ) ⇒ 正 9 - strcmp_in_numerical_order("123", "123") ⇒ 0 10 - strcmp_in_numerical_order("12", "123") ⇒ 負 11*/ 12int strcmp_in_numerical_order(const char* s1, const char* s2) { 13 return atoi(s1) - atoi(s2); 14}

不正な入力に対して脆弱(というかスルー)ですが、それはまた別の話です。
あと面倒なので桁あふれの可能性も無視してます。


cのif文の条件分岐では ... 真偽を明示すべきか

C99以降では、stdbool.hに定義されているtrue/falseを使えば良いかと思います。

コメントを受けて

おそらく関数の定義を自分が誤解していたので関数は辞書順に見て前か後ろ(または同じ)かという実装の問題ではないかと思います

私もそんな気がします。


C

bool strBefore(char *s, char *t) {
if (strcmp(s, t) <0)
return true;
else
return false;
}
bool strBefore2(char *s, char *t) {
return strcmp("1", "2")<0?true:false;
}

次のように書いた方が簡潔かと思います。

C

1bool strBefore3(char *s, char *t) { 2 return strcmp(s, t) < 0; 3}

変数名には 「より前」の翻訳で出てきたbeforeを使い

cmpよりは彩りがある言葉ですね。
しかしs.before(t)という形式で記述出来ないので、コメントは添えた方が丁寧に思います。

bool型に変えられる部分は変えてみました。

falseが0、trueが非0というのは信頼してコードを書いていいかと。
厳密には規格書に当たらないといけないですし、それに反する実装も存在し得ますが、
私にはそれを判断するだけの知見が現状無いです。すみません。

マジックナンバーはあまりよくないらしいですが、この関数では、多くの場合実装を変更する人は、strcmp()の仕様を既に理解しているか、知らなくても理解してから変更すると思われるので、「if (strcmp(s, t) <= 0)」の0をローカルだからと言って短い名前の変数にしても、strcmp()の返り値がわかっている人が見た場合、逆にコードの理解の妨げになるかと思ってつけませんでした。

その判断で正しいように思います。
返り値を三つ以上に分岐させたい場合は、列挙体の利用も視野に入れると良いでしょう。


余談ですがC言語の名前のつけ方でよりよく使われるのは次の二つのどちらでしょうか、またそれ以上に使われる名前の付け方はあるでしょうか。最もオーソドックスなやり方を覚えておきたいんです

xxxYyy(文字の区切りに大文字)、xxx_yyy(文字の区切りに_)

xxx_yyyが一般的ですね。『C言語 命名規則』でググると色々な意見が見られます。

名称Cでの用途別名
PascalCaseUpperCamelCase
camelCaselowerCamelCase
snake_case変数、関数snail_case
UPPER_SNAKE_CASEマクロUPPER_SNAIL_CASE

他にもプレフィックス(アプリケーションハンガリアン)を知っておくと彩りが増えます。
今見てもピンとこないかもしれませんが: 間違ったコードは間違って見えるようにする

あとは、動詞形と名詞形の使い分けですね。
関数に動詞、変数に名詞、配列には複数形名詞を付けると見通しが良くなります。
また、論理値を返す関数名をis, can, hasなどで始める人が多いです。

命名規則は状況によって変わり得ます。
チームで開発をしているのならチームの規則に合わせるべきです。
個人で開発をする際には、充分なメリットが説明できれば特殊な規則に従っても良いでしょう。

投稿2018/02/01 15:40

編集2018/02/07 06:07
LouiS0616

総合スコア35660

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2018/02/02 07:08

"1234"と"234"という文字列では数値も文字列の長さも"1234"の方が大きく、長い。 しかし辞書順に見た場合、文字列の先頭の"1"と"2"ではASCIIコードでは1(0x31)<2(0x32)となって後者の方が大きいので、 strcmp(""1234,"234")では-1が返されるというような理解で正しいでしょうか? 辞書順で見るというのは辞書で例えるとワ行の項目は単語の文字数が長くても絶対にア行より前にならない、 というようなことでしょうか。
LouiS0616

2018/02/02 07:21

辞書順とは一般にそういうことですね。 順序を決定する指標は分かりやすければ/必要であれば本当になんでもいいのです。
退会済みユーザー

退会済みユーザー

2018/02/02 11:35 編集

ありがとうございます 本を読んだ前提でのメタな見方になってしまうんですが、この問題の章、実装の正しさより 人に誤解されないような名前を心がけよう、という内容が多めになっていて、前問でも修正点が一か所しかなかったので おそらく関数の定義を自分が誤解していたので関数は辞書順に見て前か後ろ(または同じ)かという実装の問題ではないかと思います ちょっと書き換えてテストしてみました。 ```c #include<stdio.h> #include <string.h> #include <stdbool.h> bool strBefore(char *s, char *t){ if (strcmp(s, t) <0) return true; else return false; } bool strBefore2(char *s, char *t){ //return strcmp("1", "2")<0?true:false; 20172/2/20:34追記修正 恥ずかしい return strcmp(s,t)<0?true:false; } int main(){ printf("%d\n",strBefore("1","2")); printf("%d\n",strBefore("1", "1")); printf("%d\n",strBefore("2", "1")); printf("%d\n",strBefore2("1","2")); printf("%d\n",strBefore2("1", "1")); printf("%d\n",strBefore2("2", "1")); } 結果 1 0 0 1 0 0 ``` 変数名には 「より前」の翻訳で出てきたbeforeを使い、bool型に変えられる部分は変えてみました。 マジックナンバーはあまりよくないらしいですが、この関数では多くの場合実装を変更する人は、strcmp()の仕様を既に理解しているか、知らなくても理解してから変更すると思われるので、「if (strcmp(s, t) <= 0)」の0をローカルだからと言って短い名前の変数にしても、strcmp()の返り値がわかっている人が見た場合、逆にコードの理解の妨げになるかと思ってつけませんでした。 コードを書いている途中に三項演算子を使った方が簡潔ではないかと思い、三項演算子版関数も追加してみました。 余談ですがC言語の名前のつけ方でよりよく使われるのは次の二つのどちらでしょうか、またそれ以上に使われる名前の付け方はあるでしょうか。最もオーソドックスなやり方を覚えておきたいんです xxxYyy(文字の区切りに大文字)、xxx_yyy(文字の区切りに_)
LouiS0616

2018/02/02 09:15

返信が長くなったので回答に追記する形式にしました。ご確認ください。
退会済みユーザー

退会済みユーザー

2018/02/03 06:29 編集

詳しい説明ありがとうございます。
momon-ga

2018/02/05 01:43

あの設問は、正解不正解は重要でなく、まさにこのスレッドような検討をすることが目的なのですよねぇ。
momon-ga

2018/02/07 05:28

あ、仮引数の名前はs1、s2より "s"maller "t"hanなので、元のs、tが好みです。
guest

0

C言語、ほとんどさわってないですが・・・
strcmpを使った場合、"10"と"2"だと、どっちが小さいって判定されますか?

投稿2018/02/01 10:03

momon-ga

総合スコア4820

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2018/02/01 10:31

printf("%d\n", strcmp("555", "55")); //適当に選んだ数字です だと1 printf("%d\n", strcmp("10", "2")); だと-1と判定されますね そこらへんちょっとよくわからないです
cateye

2018/02/01 11:26 編集

・・・上の例だと '5'-'5' →'5'-'5'→'5'-'\0'・・・ > '1'-'2'・・・ < ※この辺は実装依存と思いますが、文字列長が違う場合strcmp()は、!=0あるいは==0での判定に適していると思います。
guest

0

ベストアンサー

まずstrcmpは辞書式比較ではなくバイトごと比較です。

辞書式なら(大文字が先に出てくるとするなら)

"A"< "a" < "Aa" < "B" < "b"

という大小関係にならなければいけません。
strcmpでは( ASCIIコードを使ってるマシンでは)

"A" < "Aa" <"B" < "a" < "b"

という大小関係になります。
まじめに辞書式比較をするならば、英語なら大文字小文字、日本語なら撥音拗音音引きあたりの処理がややこしいです。

あと作法の中に書いてあったかどうか忘れましたが、一般に

#define TRUE 1 #define FALSE 0 #define ZERO 0 #define BEGIN { #define END }

などは悪手とされています。たとえばZEROといっても、0,0L, 0.0, '0', "0"などなどいろいろ有るので、下手にマクロにされると逆にわかりにくくなります。あと、Cでの真偽は0と0以外 です。比較式の値が0/1になりますが。なので1だけTRUEであるかのように思える(==で真であることが判定できる)ようなまマクロはやめた方が良い、ってのもあったようなきがします。このあたり、Cのコーディングスタイるン本には詳しく載っていると思いますよ。

if(式==TRUE)と書く必要はなくてif(式)でいいとおもいますが。

余談ですが,smallerはis_smaller的な関数にも思えますし、min的(二つの引数のうち小さい方を返す)関数の名前のようにも思えますね。

#include <string.h> char *smaller(char *s, char *t) { if (strcmp(s, t) < 0) return s; else return t; }

投稿2018/02/06 02:11

編集2018/02/07 05:03
a_saitoh

総合スコア702

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

momon-ga

2018/02/06 02:43

辞書順じゃないのですね。経験者のコメントですね。 C言語うといのですが、is_slammerって、どんな関数ですか?
a_saitoh

2018/02/06 02:46

言語によりますが、引数を調べてTRUE/FALSEを返す判断関連関数(メソッド)には「isほげほげ」と命名する文化があります。Cでもisspace()とかがありますね。
momon-ga

2018/02/06 10:43

あ、isの部分でなくslammerが、よくわからなかったです。typoなら、納得なのですが・・・
a_saitoh

2018/02/07 05:02

typoです。なおしときます。
guest

0

strcmpは二つの文字列を比較し、

どちらの文字列の方が辞書順でみて若いかによって返り値が決定します。数字としての大小ではありませんし、文字の長さでもありません。

投稿2018/02/01 13:09

HogeAnimalLover

総合スコア4830

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

コメントだけして逃げるのもアレだったので自分なりの考えを。

辞書順の結果を返すstrcmpが不適切と考え、

関数名通り整数数字を比較する関数にするならば、簡単な実装としてatoiを使って書き直せばいいのかなと思いました。(整数に変換できない場合などのエラー処理は考慮していません。)

私もC言語はあまりやらないので、自信はないですけど…(^ ^;

C

1#include <stdlib.h> 2 3int smaller(char *s, char *t){ 4 return atoi(s) < atoi(t); 5}

Wandbox

投稿2018/02/01 14:37

編集2018/02/01 14:52
namnium1125

総合スコア2043

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

投稿してから関数名smallerについて文字列の数値を読み込むという趣旨のことを追加したほうがいいのではないかと思ったが文字列であることは引数に明示されているのでそこは省略して
「numSmaller」に変更したほうがいいのかもしれないと思いました

投稿2018/02/01 10:03

退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

namnium1125

2018/02/01 11:02 編集

『momon-ga様の回答は、「数値の比較ではなく文字列の比較ということを強調するべきでは?」という意味だと思います。 そう考えるとむしろ "strSmaller"もしくは"shorter"の方がいいような…確かに引数で強調されてはいますが、numSmallerにするとむしろ混乱を招く気がします。』 ごめんなさい私も勘違いしている部分がありました。このコメントは無視していただけると幸いです。お騒がせいたしました。m(_ _)m 調べた感じだとstrcmpは辞書引きした時にどちらが先に来るかを返すみたいですね。
退会済みユーザー

退会済みユーザー

2018/02/01 11:02

この問題の関数はそもそも文字列を比較する関数で自分はそれを誤解していた、ということでしょうか? そうなると 自分が変更した"if (strcmp(s, t) < 0)"という修正が適切だったかも不安になってくるんですが 変更点とそれ以外の実装には問題はないんでしょうか
shsh_

2018/02/01 11:49

strcmpはstring compare(文字列比較)の略です。
退会済みユーザー

退会済みユーザー

2018/02/01 12:48

smallerは文字列の文字数str1がstr2より短ければ真、そうでなければ偽を返す関数で、 だからnamnium1125さんが述べたように関数名としては適切でない。 それとは別にsmaller()を修正するにはstrcmp()の挙動を理解し、 必要によっては別の関数に変える必要がある、ということでしょうか 明日よく調べてみます
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.47%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問