Chuyển đến nội dung
Diễn đàn CADViet
  • Thông báo

    • Nguyen Hoanh

      CADViet đã hoàn tất nâng cấp   14/09/2017

      Chào các bạn, CADViet đã hoàn tất việc nâng cấp lên phiên bản mới. Tất cả các chức năng đã hoạt động theo kỳ vọng của ban quản trị. Nếu có vấn đề gì cần phản hồi, các bản post ở đây nhé: Trân trọng, Nguyễn Hoành.
Doan Van Ha

[Thảo luận] Code lisp như thế nào để hạn chế lỗi cho người dùng?

Các bài được khuyến nghị

Tue_NV    3.841

Mình phản đối cách bắt buộc người dùng phải nhập cho đến khi nhập thành công (bằng vòng lặp (while hay (initget. Cách này rất thô bạo và không chuyên nghiệp. Theo mình, nếu người dùng không nhập đủ thông tin thì chỉ cần kết thúc mà không làm gì là được. Giả sử người ta gõ lệnh xong, người dùng mới thấy nó chưa cần thiết, mà người ta không thể thoát ra được, thì chỉ có cách nhấn ESC và thoát ngang, như vậy rất không thuận tiện.

 

Về (command và (vl-cmdf, có vấn đề cần lưu ý là chúng không thể thay thế cho nhau hoàn toàn. Ví dụ đoạn code sau:

(command ".circle" (setq d (getpoint)) 1 ".move" (entlast) "" d pause)

Nếu thay (command bằng (vl-cmdf thì nó sẽ không thể thực hiện bình thường được! :)

 

1-/ Đồng ý

2-/ Đọc code cũng đã hiểu rồi bạn. Vl-cmdf định giá các tham số trước khi thực hiện lệnh.

(entlast) trong code trên thì trong vl-cmdf được định giá là đối tượng tạo ra cuối cùng của bản vẽ

còn trong command thì do đọc đến đâu thực thi lệnh đến đó nên (entlast) hiểu là circle vừa được tạo ra

3./ vl-cmdf còn được dùng để làm điều kiện cho hàm if

  • Vote tăng 2

Chia sẻ bài đăng này


Liên kết tới bài đăng
Chia sẻ trên các trang web khác
Doan Van Ha    2.678

1). Về vl-cmdf: tất cả những gì Ket+Polyme+Tue_NV nói đều đúng cả, và help cũng nói thế rồi. Và thậm chí nó còn khác nhau vài tí nữa, cũng có trong help.

2). @Polyme+Tue_NV: đây chỉ là ví dụ đưa ra để dẫn dắt cho 1 vấn đề bẫy lỗi mà thôi, tỉ như ví dụ này là 1 trích đoạn trong 1 lisp dài, mà nếu nhập sai đoạn đầu thì đoạn sau bị lỗi. Chứ chẳng ai đi viết 1 lisp kiểu cực đoan như vậy, và chắc sẽ bị user ném đá ngay thôi.

Tóm lại: tôi vẫn mong muốn mọi người tìm cách bẫy lỗi theo y/c đề bài, vì qua mấy comment cũng chưa tìm ra được cách nào tốt cả.

  • Vote tăng 1

Chia sẻ bài đăng này


Liên kết tới bài đăng
Chia sẻ trên các trang web khác
gia_bach    1.442

Mình phản đối cách bắt buộc người dùng phải nhập cho đến khi nhập thành công (bằng vòng lặp (while hay (initget. Cách này rất thô bạo và không chuyên nghiệp. Theo mình, nếu người dùng không nhập đủ thông tin thì chỉ cần kết thúc mà không làm gì là được. Giả sử người ta gõ lệnh xong, người dùng mới thấy nó chưa cần thiết, mà người ta không thể thoát ra được, thì chỉ có cách nhấn ESC và thoát ngang, như vậy rất không thuận tiện.

.....

Có gì đó không ổn rồi!

Hãy xem cách Cad làm việc với lệnh Circle :

+ khi yêu cầu nhập bán kính nếu User nhập số âm hoặc bằng 0, Cad sẽ yêu cầu nhập lại ......và nhập lại cho tới khi giá trị là số dương. 

-> như vậy bạn gọi CAD là " rất thô bạo và không chuyên nghiệp" ?

 

Giả sử môt Lisp yêu cầu nhập khoảng 10 giá trị đầu vào, khi nhập đến giá trị thứ 4-5 thì phát hiện giá trị nhập trước đó là sai ?

-> nhấn ESC và nhập lại từ đầu.

Một Lisp khác sẽ kiểm tra ngay từ đầu giá trị hợp lệ khi nhập (VD : phải là số dương) sau đó mới cho nhập tiếp giá trị thứ 2,thứ 3, ....

Lisp nào sẽ chuyên nghiệp hơn, ít mất thời gian hơn, hiệu quả hơn ??

  • Vote tăng 2

Chia sẻ bài đăng này


Liên kết tới bài đăng
Chia sẻ trên các trang web khác
Polyline    18

@gia_bach: Có vẻ như bác đã quá lời rồi! Theo mình, khi một hàm nào đó yêu cầu từ 4 số liệu đầu vào trở lên thì nên nghĩ ngay đến phương án giao diện hộ p thoại, ở đó người dùng có thể nhập 1 lần tất cả các tham số theo bất kỳ thứ tự nào. Bác đặt ra giả sử đến 10 tham số đầu vào trong một lisp thì rất xa lạ đối với AutoCAD rồi!

 

#13: Mình thử gán (setq p "a") trước khi chạy lệnh nhưng nó vẫn thực hiện ngon ơ! Như vậy, nếu một hàm có khai báo biến cục bộ, khi gặp biến toàn cục thì nó vẫn lấy theo biến cục bộ, sau đó hoàn trả lại giá trị của biến toàn cục sau khi thực hiện xong - Điều này có thể các bác đã biết nhưng với mình đây là điều mới mẻ => Việc đặt biến cục bộ không những giải phóng bộ nhớ mà còn giúp tránh xung đột về biến trùng tên.

Chia sẻ bài đăng này


Liên kết tới bài đăng
Chia sẻ trên các trang web khác
tien2005    97

mình code lại vẽ hình tròn. Các Bạn góp ý thêm

(defun c:vht ( / p r)
(while (not (setq p (getpoint "\nSpecify center point for circle: "))))
  (while (or (not r) (equal r 0.0 0.00001))
    (setq r (getdist "\nSpecify radius of circle: " p))
    )
 (vl-cmdf "circle"  p (abs r))
 (princ))

 

 

Chia sẻ bài đăng này


Liên kết tới bài đăng
Chia sẻ trên các trang web khác
gia_bach    1.442

@gia_bach: Có vẻ như bác đã quá lời rồi! Theo mình, khi một hàm nào đó yêu cầu từ 4 số liệu đầu vào trở lên thì nên nghĩ ngay đến phương án giao diện hộ p thoại, ở đó người dùng có thể nhập 1 lần tất cả các tham số theo bất kỳ thứ tự nào. Bác đặt ra giả sử đến 10 tham số đầu vào trong một lisp thì rất xa lạ đối với AutoCAD rồi!

.....

Chỉ là minh họa thôi. Nhưng thực tế thì vẫn có lisp y/cầu nhập trên 10 tham số đầu vào (VD: vẽ mặt cắt + kích thước).

Cho dù có dùng hộp thoại thì vẫn phải viết hàm bẫy lỗi cho nó (VD: tham số chiều dài mà user lại nhập chữ )

Hơn nữa cá nhân tôi cho rằng : nhập dữ liệu từ hộp thoại tốc độ nhập không nhanh bằng nhập trực tiếp từ dòng lệnh ?!

 

Tuy nhiên với 1 lisp chỉ y/cầu nhập 2 tham số, nếu 1 trong 2 tham số nhập sai thì user cũng phải nhập lại từ đầu. 

VD: với lệnh Trim mở rông tham khảo http://www.cadviet.com/forum/topic/71578-da-xong-lenh-trim-mo-rong/

(sorry HÀ nhé)

- khi yêu cầu Chon 1 doi tuong dao cat: nếu user chọn nhầm đường gióng kích thước (dễ nhầm lẫn với Line) thì các thao tác chọn sau đó sẽ vô nghĩa và tốn th/gian vô ích.

 

 

@Hà:

Với lệnh VHT tại dòng nhắc Pick diem 1:  nếu user nhập chỉ 1 giá trị số (VD : 15) không phải là tọa độ điểm thì lệnh vẫn vẽ ra đường tròn nhưng tâm đuòng tròn sẽ là 1 điểm nào đó mà qui luật của nó cần các Lisper nghiên cứu thêm.

  • Vote tăng 1

Chia sẻ bài đăng này


Liên kết tới bài đăng
Chia sẻ trên các trang web khác
Doan Van Ha    2.678

@Bác Gia_bach:

1). Màu tím số 1: đã bổ sung để hạn chế bớt lỗi ở bên đó.

2). Màu tím số 2: vâng, getpoint chấp nhận cả int và real. Nó liên quan lastpoint. Vấn đề ở đây chỉ là 1 ví dụ để bẫy lỗi thôi.

Thank bác!

Thêm ý này: nhập từ command thường nhanh hơn dialog, nhưng nhập từ dialog dễ kiểm soát hơn.

@Polyme: tôi đã từng thiết kế dialog, nhưng cũng có cái phải nhập trong dialog một số lớn các thông số, đồng thời phải nhập từ command cũng 1 số lớn thông số.

@Tien2005: dùng abs thì không lỗi nhưng hơi cưỡng ép nhỉ?

  • Vote tăng 1

Chia sẻ bài đăng này


Liên kết tới bài đăng
Chia sẻ trên các trang web khác
Doan Van Ha    2.678

Giải pháp bẫy lỗi đề xuất cho bài #12. Ai có giải pháp hay hơn thì đề xuất nhé?


(defun C:VHT( / p r)
 (if
  (and
   (not (initget 1))
   (setq p (getpoint "\nSpecify center point for circle: "))
   (not (initget 7))
   (setq r (getdist p "\nSpecify radius of circle: ")))
  (command ".circle" "non" p r))
 (princ))
 

Chia sẻ bài đăng này


Liên kết tới bài đăng
Chia sẻ trên các trang web khác
Doan Van Ha    2.678

Mời các bạn đọc bài #1 đã được update, và sẽ liên tục được update...

Đồng thời, mời các bạn xem lisp dưới đây có thể bị lỗi hay không. Từ đó chúng ta sẽ rút ra kết luận.


(defun C:HA()
 (or a (setq a 1))
 (setq a (cond ((getint (strcat "\nNhap 1 so nguyen <" (itoa a) ">: "))) (a)))
 (1+ a))
 

Chia sẻ bài đăng này


Liên kết tới bài đăng
Chia sẻ trên các trang web khác
ketxu    2.653

Không kể đến ngắt đột ngột thì a là biến toàn cục, hoàn toàn có thể bị thay đổi thông tin bởi Lisp khác (thành chuỗi, thành số thực ...) , nếu cẩn thận thì đặt tên biến hơi dị một chút, và kiểm tra điều kiện về a (thêm ở chỗ or a )

  • Vote tăng 1

Chia sẻ bài đăng này


Liên kết tới bài đăng
Chia sẻ trên các trang web khác
Doan Van Ha    2.678

Cám ơn Ketxu! Đúng là như thế. Và một giải pháp để sửa lại lisp trên, tránh bị lỗi do tàn dư của lisp khác, có thể như thế này.

(Lý thuyết về vấn đề này sẽ được bổ sung ở bài đầu tiên #1).


(defun C:HA()
 (or (and a (= (type a) 'int)) (setq a 1))
 (setq a (cond ((getint (strcat "\nNhap 1 so nguyen <" (itoa a) ">: "))) (a)))
 (1+ a))
 

  • Vote tăng 1

Chia sẻ bài đăng này


Liên kết tới bài đăng
Chia sẻ trên các trang web khác
Doan Van Ha    2.678

Chúng ta trở lại với bài vẽ Circle bằng 1 lỗi khác. Lỗi này thường ít được để ý và nếu không để ý thì sẽ khó phát hiện.

Mời mọi người tìm lỗi sau đây, và đề xuất phương án bẫy lỗi.

[Chú ý: các lỗi sẽ update liên tục ở bài #1].


(defun C:VHT( / p r)
 (if
  (and
   (not (initget 1))
   (setq p (getpoint "\nSpecify center point for circle: "))
   (not (initget 7))
   (setq r (getdist p "\nSpecify radius of circle: ")))
  (entmake (list '(0 . "Circle") (cons 10 p) (cons 40 r))))
 (princ))
 

Chia sẻ bài đăng này


Liên kết tới bài đăng
Chia sẻ trên các trang web khác
lyky    126

Doan Van Ha, on 31 May 2013 - 14:09, said:

(defun C:HA()
(or (and a (= (type a) 'int)) (setq a 1))
(setq a (cond ((getint (strcat "\nNhap 1 so nguyen <" (itoa a) ">: "))) (a))) (1+ a))
;;; Tuy nhien, neu:
(defun C:HA( / a)....)
;;; Thi gia tri tham khao khong duoc luu lai!

Cách này chẳng những hạn chế được lỗi, mà còn rất tiện dụng trong trường hợp giá trị nhập thường được lặp lại, chúng ta có thể sắp xếp thành một hàm con dùng để nhập số nguyên "an toàn" hơn và nếu có thể, thay thế hàm getint, như sau:

(defun TgI (RV MSG REF / S U V)
(setq s (if msg msg (strcat "\nNhap so nguyen " (vl-symbol-name rv))))
(setq v (eval rv) u (if (= (type v) 'int) v ref))
(setq v (getint (strcat s " [" (itoa u) "]:")))
(set rv (if v v u)))
;;;........................Vi du;;;
(TgI 'sn "Vo Nguyen Giap" 19110825)
  • Vote tăng 1

Chia sẻ bài đăng này


Liên kết tới bài đăng
Chia sẻ trên các trang web khác
Tue_NV    3.841

Chào bạn Lyky!

Bạn viết nếu (defun c:HA(/ a) .... thì giá trị tham khảo a không được lưu giữ lại.

 

Nhưng nếu hàm bạn viết là (defun TgI (RV MSG REF / S U V SN);;;;;; thì với ví dụ bạn viết là (TgI 'sn "Vo Nguyen Giap" 19110825) thì biến SN cũng không được lưu giữ lại mà.

Mình thấy "an toàn" như nhau. 

  • Vote tăng 1

Chia sẻ bài đăng này


Liên kết tới bài đăng
Chia sẻ trên các trang web khác
Doan Van Ha    2.678

Cám ơn bạn Lyky+Tue_NV đã có ý kiến trao đổi.

1). Để "ghi nhớ" cái gì đó cho lần sau thì: hoặc không được khử nó (lisp DVH - với a), hoặc không thể khử nó (lisp Lyky - với sn) >> làm gì có chuyện nó vừa cục bộ vừa toàn cục nhỉ? 

2). Nếu để tạo 1 hàm nhập liệu vừa bẫy lỗi vừa lưu biến như ý Lyky thì liệu cái này có gọn nhẹ hơn không?


(defun HA(def)
 (or (and (= (type a) 'int) (< -32769 a 32768)) (setq a def))
 (setq a (cond ((getint (strcat "\nNhap so nguyen <" (itoa a) ">: "))) (a))))
;EX: (HA 1)
 

Chia sẻ bài đăng này


Liên kết tới bài đăng
Chia sẻ trên các trang web khác
lyky    126

Cám ơn bạn Lyky+Tue_NV đã có ý kiến trao đổi.

1). Để "ghi nhớ" cái gì đó cho lần sau thì: hoặc không được khử nó (lisp DVH - với a), hoặc không thể khử nó (lisp Lyky - với sn) >> làm gì có chuyện nó vừa cục bộ vừa toàn cục nhỉ? 

2). Nếu để tạo 1 hàm nhập liệu vừa bẫy lỗi vừa lưu biến như ý Lyky thì liệu cái này có gọn nhẹ hơn không?

 

Bạn hiểu nhầm ý lyky mất rồi! lyky muốn tạo một hàm con, dùng nhập số nguyên trong các lisp khác thay hàm getint mà, có một số khác nhau chứ:

 

1. Nếu dùng hàm (HA):

a/- Dòng nhắc luôn cố định là “Nhập số nguyên <gợi ý>:” , một hàm con sẽ thân thiện hơn nếu dòng nhắc “mềm” hơn, ví dụ trong một chương trình nào đó, muốn bạn nhập số con của bạn (chẳng hạn!) mà dòng nhắc: “Nhập số nguyên <gợi ý>:” thì kỳ kỳ. Nên lyky mới thêm ưu tiên nếu có MSG, nếu không có mới trở về mặc định!

b/- Hàm chỉ dùng một biến toàn cục a, vì vậy, mặc dù tiết kiệm dung lượng, nhưng nó không mang tính “rẽ nhánh”, ví dụ: có 2 lisp cùng dùng hàm (HA) để nhập số nguyên → khi lisp1 chạy, lưu lại biến toàn cục a, tiếp tục chạy lisp2: a lại đưa lên làm gợi ý, tuy nhiên, gợi ý đối với lisp1 và lisp2 thì thường khác nhau! Vì vậy, gợi ý đó không xác thực lắm!

 

2. Nếu dùng hàm (TgI ‘a “msg” <gợi ý>):

c/- Dòng nhắc tùy nhu cầu cụ thể mà thay đổi theo cho phù hợp, khi không cần thay đổi thì “msg” = nil, mặc dù dài loằn ngoằn, nhưng số bước chạy không nhiều hơn mấy?!

d/- (TgI) đưa biến toàn cục ra ngoài hàm con này, do đó, đối với mỗi lisp có dùng nó sẽ có một biến toàn cục riêng, và vì vậy mà nó “rẽ nhánh” tốt hơn, khi chạy nhiều lisp có dùng nó sẽ tránh bị tương tác như trên! Mặc dù rằng, số biến toàn cục nhiều hơn - nếu bạn chấp nhận được!

 

Mấy lời phân trần, mong các bác vui! Chúc các bác thật nhiều sức khỏe và thành công trong công tác nhé!

 

@ Bác Doan Van Ha: Topic thật tuyệt diệu, đặc biệt là có được một người “thư ký” kiêm “điều hành” mẫn cán! Mong và hi vọng thật nhiều điều hay và kinh nghiệm các bác chia sẽ cùng anh em!

  • Vote tăng 1

Chia sẻ bài đăng này


Liên kết tới bài đăng
Chia sẻ trên các trang web khác
gia_bach    1.442

Chúng ta trở lại với bài vẽ Circle bằng 1 lỗi khác. Lỗi này thường ít được để ý và nếu không để ý thì sẽ khó phát hiện.

Mời mọi người tìm lỗi sau đây, và đề xuất phương án bẫy lỗi.

[Chú ý: các lỗi sẽ update liên tục ở bài #1].

 

(defun C:VHT( / p r)
 (if
  (and
   (not (initget 1))
   (setq p (getpoint "\nSpecify center point for circle: "))
   (not (initget 7))
   (setq r (getdist p "\nSpecify radius of circle: ")))
  (entmake (list '(0 . "Circle") (cons 10 p) (cons 40 r))))
 (princ))
 

Có lỗi xảy ra khi hệ toạ độ hiện hành không ở WCS.

 

khắc phục: thay (cons 10 p) bằng (cons 10 (trans p 1 0))

  • Vote tăng 2

Chia sẻ bài đăng này


Liên kết tới bài đăng
Chia sẻ trên các trang web khác
Doan Van Ha    2.678

Cám ơn bác Gia_bach. Lỗi này đã được update ở bài #1.

Đang chờ đợi các bạn post lên những lisp (ngắn gọn) có thể bị lỗi để mọi người cùng học hỏi. Hiện nay, list error đang ngày càng dày thêm rồi đó.

Hoặc các bạn có thể gởi tin nhắn cho tôi về 1 lỗi nào đó, tôi sẽ tổng hợp để đưa lên forum theo hình thức đã và đang làm để chúng ta cùng học hỏi.

Trân trọng và chờ đợi!

@Lyky: cám ơn bạn đã động viên.

  • Vote tăng 1

Chia sẻ bài đăng này


Liên kết tới bài đăng
Chia sẻ trên các trang web khác

Tạo một tài khoản hoặc đăng nhập để nhận xét

Bạn cần phải là một thành viên để lại một bình luận

Tạo tài khoản

Đăng ký một tài khoản mới trong cộng đồng của chúng tôi. Điều đó dễ mà.

Đăng ký tài khoản mới

Đăng nhập

Bạn có sẵn sàng để tạo một tài khoản ? Đăng nhập tại đây.

Đăng nhập ngay


×