Dec
7
2010

Thảo luận về bài tập 5

Đặt ra nguyên tắc thì phải tuân theo. Nhưng cuộc sống luôn có những ngoại lệ. PHP cũng có ngoại lệ (các bạn sẽ được học sau). Và ngày hôm nay, tôi xin phép các bạn được sử dụng một ngoại lệ…

Đó là việc tôi sẽ mang bài làm của bạn Bambi Nguyễn ra như một ví dụ mẫu để comment và sửa chữa. Bạn Bambi không phải người nộp bài sớm nhất, cũng chưa xuất sắc nhất nhưng hôm nay là sinh nhật của Bambi, nên sửa bài của Bambi như một lời chúc mừng sinh nhật. Chúc Bambi học giỏi, vui vẻ và may mắn :-)

image

Bài 5.1:

Giải thích: Khi user bắt đầu nhập giá trị cho $a (vd: $a = 34) rồi nhấn phím enter để nhập típ giá trị cho $b(vd: 56). Vô tình chức năng enter đó được lưu vào với ký hiệu ’ \n’,vì thế trình thông dịch sẽ hiểu là 34\n+ 56\n,khi đó kết quả xuất ra màn hình cũng kèm theo ‘\n’.

Vì thế, để kết quả xuất ra màn hình ko kèm theo ‘\n’ ta buộc phải ép kiểu giá trị nhập vào sang kiểu int,vì kiểu int ko chấp nhận ký tự khác.

Bình luận của tôi: Bạn trả lời đúng. Trình thông dịch nhận được kết quả nhập vào là “34\n + 56\n” nên khi hiển thị nó sẽ thay \n bằng ký tự xuống hàng. Tuy nhiên, để khi xuất dữ liệu không bị nhảy con trỏ xuống hàng thì chúng ta có nhiều cách, một trong những cách là đó là ép kiểu giá trị nhập vào về kiểu số. Không nhất thiết / và không nên là kiểu nguyên. Thay vào đó, các bạn có thể ép nó về kiểu thực thì hợp lý hơn. Vì khi tính tổng 2 số, bạn mặc nhiên coi đó là 2 số thực thì sẽ bao hàm và rộng hơn khi coi nó là 2 số nguyên. Đây chỉ là vấn đề suy luận logic, không có quy định trong bài nên không có đúng sai. Câu kiểu nguyên không chấp nhận ký tự khác khá mơ hồ Bambi ạ, bạn nên nói rõ hơn. Vì quả tình tôi cũng không hiểu bạn nói ko chấp nhận ký tự khác là ký tự khác nào?

clip_image002

Với bài này, bạn Việt DHT – người gửi bài làm sớm nhất – có câu trả lời hợp lý:

…nếu muốn kết quả hiện ra đẹp hơn. Ta sẽ ép kiểu $a,$b về kiểu int hoặc float tùy yêu cầu của bài toán. Như vậy ta có thể loại bỏ được được phím Enter ở sau cùng.

Riêng bạn Scott Lee thì có một bài làm rất chuyên nghiệp và chi tiết. Vì bài của bạn quá dài nên tôi để ở phía cuối cho khỏi loãng. Các bạn kéo xuống dưới cùng để tham khảo nhé.

Bạn Nguyễn Quang Phước đưa ra câu hỏi: “có một điều em không hiểu là tại sao khi em chưa đổi kiểu, $a và $b đều là kiểu string mà khi cộng lại vẫn cho ra kết quả đúng là 90. (string + string = int)”.

Tôi cho rằng đây là một câu hỏi rất hay, và “vô tình” bài của bạn Scott Lee đã trả lời cho câu này rất rõ ràng và chi tiết rồi Phước ạ. Em tham khảo ở dưới cùng nhé.

Lê Đình Nam có câu trả lời xuất sắc, mời mọi người tham khảo:

Ý thứ nhất là giải thích tại sao dấu + và dấu = lại rơi xuống dòng dưới. Khi nhập giá trị cho biến bằng fgets, thì giá trị trả về của biến đó sẽ là 1 string kết thúc bằng kí tự enter. Vì thế khi xuất biến đó ra màn hình ta sẽ bị tình trạng xuống dòng. Sau đây là 1 ví dụ nhỏ để kiểm tra .

clip_image001

clip_image003[5]

Ta thấy là dù không nhập giá trị nào thì biến của ta vẫn có kiểu string<2>. Em nghĩ(khá chắc chắn) 1 kí tự là Enter, còn kí tự kia có thể là NULL :P. Còn tại sao có tận 2 kí tự như thế thì xin anh giải thích thêm.

Và để giải quyết cái vụ xuống dòng bừa bãi này, thì em dùng ‘ép kiểu’, ép từ kiểu string thành kiểu int và như thế kí tự enter cũng ‘disappear’ luôn :D.

clip_image005

Phát hiện thú vị phải không ạ? Các bạn đều đề cập tới ký tự <Enter> nhưng riêng bạn Nam thì nhận ra chuỗi bị dư ra 2 ký tự (ở bài 5.3, nhiều bạn tìm cách copy chuỗi và bỏ đi 2 ký tự sau cùng nhưng không giải thích được tại sao :D)

Tôi trả lời, sở dĩ khi bạn không nhập gì mà PHP cũng hiển thị kiểu string có 2 ký tự vì “không nhập gì” chính là bạn gõ Enter ngay khi được hỏi. Và Enter trong PHP được biểu thị bởi một cặp 2 ký tự đi cùng nhau là #13#10 (xuống hàng, về đầu hàng tiếp theo).

Bài 5.2:

clip_image004

Bình luận của tôi: Chết thật. Bạn Bambi quên mất công thức tính diện tích hình tròn là bình phương bán kính nhân pi rồi :-( Bạn chỉ lấy bán kính nhân pi thì làm sao ra kết quả đúng được ạ.

Mà tôi nói bạn khai báo hằng thì bạn lại khai báo biến :-)

Tiếp nữa – cũng là về mặt logic – thì trước khi lấy giá trị bán kính để tính ra diện tích, bạn nên ép kiểu nó về số thực cho tổng quát.

Tôi tìm lại các bài tập từ người gửi đầu tiên, hầu như bài nào cũng làm đúng cả nhưng không thấy bạn nào ép kiểu về số thực. Cho tới bài của bạn Phạm Đình Thanh Quang:

<?php
define(PI,3.141592654);
echo “———–Chuong trinh tinh dien tich hinh tron ——————\n”;
echo ‘Nhap vao mot so bat ki: ‘;
$r = fgets(fopen(‘php://stdin’,’r’));
// Ep kieu $r thanh kieu so thuc
$r=(float)$r;
echo “Dien tich hinh tron voi ban kinh R=” , $r , ” la:\n”;
echo ‘S= ‘ . $r*$r*PI;
?>

Very good Mr Quang :-) Comment cho Quang tí xíu là câu lệnh

echo “Dien tich hinh tron voi ban kinh R=” , $r , ” la:\n”;

Nếu em đã dùng dấu nháy kép như vậy thì cứ đưa $r vào trong chuỗi chứ không cần tách ra nhiều thành phần như thế:

echo “Dien tich hinh tron voi ban kinh R = $r  la:\n”;

Bạn Trịnh Mạnh Hùng cũng làm đúng, và có thêm cách hiển thị dữ liệu ra màn hình khác “đặc sắc”. Các bạn tham khảo nhé:

image

Bạn Nguyễn Hữu Long thì rất năng nổ khi “tiện thể” tính luôn cả diện tích xung quanh và diện tích toàn phần (tôi thì quên mất tiêu mấy công thức này nên không check được coi bạn tính đúng hay sai :D)

<?php
echo ‘Chuong trinh tinh dien tich hinh lap phuong khi biet chieu dai 1 canh’,”\n”;
echo ‘Nhap chieu dai canh a = ‘; $a = fgets(fopen(‘php://stdin’,’r’));
$a = trim($a);
echo ‘dien tich xung quanh Sxq = ‘,$a*$a*4,”\n”;
echo ‘dien tich toan phan Stp = ‘,$a*$a*6;
?>

Bài 5.3:

clip_image006

Bình luận của tôi: Bạn Bambi làm bài này rất tốt :-) Để “hợp logic” hơn nữa, bạn có thể ép kiểu của biến $d về kiểu nguyên trước khi lấy 2010 trừ đi $d.

Bài làm của bạn Phạm Đình Thanh Quang cho bài này rất “mẫu mực”:

<?php
echo “Nhap ten cua ban: \n”;
$ten=fgets(fopen(‘php://stdin’,’r’));
$chuoi=substr($ten,0,strlen($ten)-2);

echo “Nhap ngay sinh cua ban: \n”;
$ngay=(int)(fgets(fopen(‘php://stdin’,’r’)));
echo “Nhap thang sinh cua ban: \n”;
$thang=(int)(fgets(fopen(‘php://stdin’,’r’)));
echo “Nhap nam sinh cua ban: \n”;
$nam=(int)(fgets(fopen(‘php://stdin’,’r’)));

// Xuat thong tin
echo ‘Xin chao ban ‘ . $chuoi . ‘, ban sinh ngay ‘ . $ngay . ‘/’ . $thang . ‘/’ . $nam . ‘. Nam nay ban duoc ‘ , 2010-$nam;

?>

Thực ra, tôi không yêu cầu các bạn khi làm bài này phải ghi họ tên và các thông tin khác trên cùng một dòng đâu. Chỉ là khi ra đề bài, tôi quên tính đến chuyện dữ liệu nhập vào sẽ bị “dính” phím Enter, đâm ra vô tình thành “đánh đố”.

Bạn Quang tìm kiếm được trên mạng hàm substr để lấy một phần chuỗi ký tự nhập vào (bạn lấy từ vị trí số 0, và lấy tổng số ký tự bớt đi 2 ký tự). Tôi sẽ không giải thích sâu về việc này. Khi học kỹ hơn về chuỗi và các hàm thao tác chuỗi chúng ta sẽ quay lại vấn đề này.

Hiện thời, tôi sẽ chia sẻ ngắn gọn rằng chuỗi nhập vào, các bạn có thể dùng hàm trim(…) để xoá ký tự Enter của nó như sau:

<?

echo “Nhap ten cua ban: \n”;
$hoten=fgets(fopen(‘php://stdin’,’r’));
$hoten=trim($hoten);

?>

Bạn Phan Quốc Thắng nói rằng đã làm đủ mọi cách mà không xoá được phím Enter nên search Google tìm ra hàm chop. Quả tình tôi cũng chẳng nhớ hàm này dùng để làm gì nên có lên php.net coi lại thử thì thấy nói hàm chop dùng để xoá các ký tự “thừa” ở cuối chuỗi. Nhân tiện ý này của bạn Thắng, tôi giải thích thêm chút về trim.

PHP có hàm ltrim (left trim) và rtrim (right trim) dùng để xoá những ký tự “thừa” bên trái và bên phải chuỗi. Đặc biệt, hàm trim thì xoá cả trái và phải. Hàm chop là một tên gọi tương đương của hàm rtrim. Vậy ta có các hàm:

ltrim($a)

rtrim($a) = chop($a)

trim($a) = ltrim(rtrim($a))  hoặc = rtrim(ltrim($a)) cũng được

trim($a) = ltrim(chop($a))  hoặc = chop(ltrim($a)) cũng được

Chia sẻ ngoài lề một chút, là khi muốn tra cứu ý nghĩa của một hàm nào đó, các bạn chỉ cần gõ php.net/tên_hàm là được. Ví dụ php.net/chop ; php.net/trim

Trong bài làm của bạn Scott Lee có một ý khá hay, khi bạn định nghĩa năm 2010 thành một hằng như thế này:

Bài 5.3:
<?php
// Current year
define(Current, 2010);
// Ho ten

// Ouput result:
echo ‘Xin chao ‘, $name, ‘Ban sinh ngay ‘, $date, ‘/’, $month, ‘/’, $year, ‘. Nam nay ban duoc ‘, Current-$year,’ tuoi.’;
?>

Những gì chúng ta đang nói ở đây về việc định nghĩa hằng của bạn Scott Lee không phải nói tới việc đúng hay sai, mà nói tới “Kỹ thuật lập trình”. Nhất thời tôi không nghĩ ra được một câu nói khả dĩ tổng quát để chia sẻ cùng các bạn, nhưng trong những trường hợp “kiểu thế này”, tôi cũng sẽ định nghĩa hằng (hoặc biến) ở đầu chương trình như bạn Scott Lee.

Tại vì con số 2010 là một con số không cố định. Nếu chúng ta viết trực tiếp 2010 vào trong code là 2010 – $year thì kết quả chỉ đúng với năm nay thôi mà năm sau thì không đúng nữa. Khi ấy các bạn sẽ phải thay 2010 thành 2011. Việc này không khó trong phạm vi bài tập 5.3; nhưng nếu đây là một bài tập lớn hơn, và có 100 lần bạn phải sử dụng đến con số năm 2010 như vậy thì mỗi lần thay đổi sẽ là cả một “cực hình”. Còn nếu làm theo cách của bạn Scott thì chỉ cần sửa lại đúng 1 lần định nghĩa hằng Current thì code sẽ chạy đúng toàn bộ :-)

Bạn Lưu Minh Phương cũng làm tương tự – very good ạ, mỗi tội bạn nộp sau nên không được mang ra làm ví dụ 😀

Bạn Quang Phước cố gắng “đi tìm chân lý” bằng cách đặt ra câu hỏi (tôi rất thích cách mà bạn Phước tự đặt ra câu hỏi). Bạn Phước gán $hoten1 = ‘Phuoc’; rồi bạn lại nhập $hoten2 = fgets(fopen(‘php://stdin’,’r’)); (khi chạy chương trình bạn cũng đưa vô chữ Phuoc). Sau đó bạn var_dump($hoten1) và var_dump($hoten2) để tìm xem có gì khác biệt.

image

Tiếc là cách làm, cách thử, cách tìm tòi thì rất ổn nhưng bạn Phước lại chưa rút ra được kết luận.

Nếu nhìn vô hình sẽ thấy ngay $hoten1 của Phước được tạo ra bằng khai báo chuỗi thì không có \n ở cuối còn $hoten2 do nhập từ bàn phím thì có \n khiến bị xuống dòng, có thể do Phước không nhìn kỹ :-)

Bài 5.4:

clip_image008

Bình luận của tôi: Tương tự như các bài trên, Bambi nên ép kiểu của biến $a về kiểu thực. Còn cách làm và kết quả bài tập thì Bambi đã đúng.

Bài của bạn Phạm Đình Thanh Quang thì có lưu ý vấn đề này:

<?php
echo “Nhap vao mot canh cua hinh lap phuong: \n”;
$a=(float)fgets(fopen(‘php://stdin’,’r’));
echo “The tich cua hinh lap phuong voi canh a=” . $a . ” la: ” . $a*$a*$a;
?>

Các bạn có thể download bài làm của bạn Quang tại đây: http://www.box.net/shared/yi8hzytraa

TOÀN BỘ BÀI CỦA CÁC BẠN KHÁC CÓ ĐÚNG CÓ SAI TRÙNG VỚI NHỮNG ĐIỀU TÔI COMMENT Ở ĐÂY RỒI NÊN XIN PHÉP KHÔNG COMMENT RIÊNG NHÉ. CÁC BUỔI TRƯỚC CHỈ CÓ KHOẢNG 30 BÀI TẬP GỬI VỀ, TÔI KHÔNG HIỂU SAO LẦN NÀY CÓ TỚI GẦN 100 BÀI??? TÔI SẼ NGHĨ MỘT CÁCH NÀO ĐÓ ĐỂ CÓ THỂ GIẢI QUYẾT RỐT RÁO VIỆC COMMENT / ĐỌC BÀI CHO CÁC BẠN.

@ Đỗ Trọng An: anh rất xin lỗi nhưng không thể comment bài 4 cho em trong thời gian này được, vì trễ quá rồi.

@ Doi Thanh Thinh:

Bài tp 5.1 Hãy giải thích tại sao trong ví dụ tính tổng 2 số, khi hiển thị kết quả ra màn hình, dấu + sau 34 và dấu = sau 56 lại bị rơi xuống hàng dưới? Hãy sửa lại source code để kết quả in ra “đẹp hơn” như sau:

Explain:

$a=Hàm fgets(fopen(‘php://stdin’,’r’)) : sẽ đọc 1 chuỗi nhập từ bàn phím và kết thúc khi gặp kí tự enter.

Giá trị nhập vào sẽ được lưu vào biến $a ( trong bộ đệm ). Bây giờ ta đọc từ bộ đệm , và lấy ra các giá trị . Giá trị lấy ra có kèm theo cả dấu \n . Vì vậy nó có kết quả như hình vẽ .

Solution:

Thay vì dùng hàm echo để in ra thì em dùng hàm printf() : printf(“%2d+%2d=%2d”,$a,$b,$a+$b);

( Ko hiểu tại sao khi dùng hàm printf() áp dụng chuỗi lại sai , bài 5.3 . Anh giúp giải thích giúp em với ! )

Bạn ơi, tôi không dùng và không nghiên cứu hàm printf nên không trả lời câu hỏi của bạn được. Tôi không biết, nên rất sorry bạn 😀

Bài của bạn Scott Lee

Bài 5.1: Giải thích:

Trong đoạn hướng dẫn của anh có câu sau: “Dòng màu cam là cú pháp để chờ người dùng nhập dữ liệu vào từ bàn phím (kết thúc bởi phím Enter).” Một chi tiết nữa, đó là đoạn code dùng để nhận dữ liệu từ bàn phím: $a = fgets(fopen(‘php://stdin’,’r’)); Vì PHP và ngôn ngữ lập trình C có một số điểm tương đồng nhất định, do đó hàm fgets() ở đây dùng để lấy dữ liệu kiểu chuỗi từ một nguồn bên ngoài (thông thường là file) nhưng ở đây là lấy từ bộ đệm stdin (cũng giống như C). Kết quả trả về từ hàm này là một chuỗi. Thêm nữa, kết thúc việc nhập dữ liệu từ bàn phím là phím ENTER, do đó, chuỗi trả về cho $a sẽ bao gồm kí tự xuống dòng ‘\n’. Khi xuất ra thì php lần lượt xuất chuỗi theo thứ tự trái sang phải, gặp kí tự ‘\n’ thì tự động xuống dòng và in tiếp. Nếu em không giải thích về kết quả xuất ra vẫn đúng thì chắc chắn anh sẽ hỏi :D. Em giải thích việc này thông qua các hình ảnh sau:

clip_image001

Kết quả của chương trình trên:

clip_image003

Thêm 2 hình ảnh khác để bổ sung phần giải thích

clip_image004

Và kết quả:

clip_image006[5]

Từ đó, kết luận, ghi gặp phép cộng 2 (hoặc nhiều) biến, PHP sẽ kiểm tra xem có thể chuyển các biến trong phép cộng trên sang một kiểu số học nào đó được không (int, float,…). Sau đó, sẽ tiến hành cộng giá trị của các biến này lại. Trong trường hợp cả 2 đều không convert được thì kết quả trả về là 0, nếu 1 trong 2 biến convert được thì tùy vào các trường hợp như trong bài 4 vừa rồi đã nêu.

Sửa lại để in ra cho đẹp:

clip_image007

Leave a comment