Mô-đun hoàn toàn C: Phân loại


8

Bạn là giáo sư khoa học máy tính giảng dạy ngôn ngữ lập trình C. Một nguyên tắc bạn tìm cách truyền đạt cho sinh viên là tính mô đun . Thật không may, các lớp học trước đây có xu hướng không nhận được tin nhắn, gửi bài tập với toàn bộ chương trình bên trong main(). Do đó, trong học kỳ này, bạn đã ban hành các hướng dẫn mô đun nghiêm ngặt theo đó học sinh sẽ được xếp loại.

Một tập hợp con của ngữ pháp C và các quy tắc cho đơn vị dịch "được hình thành tốt" được định nghĩa dưới đây. Mã tuân theo các quy tắc này phải hợp lệ C89, KHÔNG GIỚI HẠN một mã định danh là từ khóa được sử dụng.

Bài tập

Bạn sẽ nhận được đầu vào là một chuỗi có chứa mã C. Bạn có thể cho rằng chuỗi này chỉ chứa khoảng trắng, dòng mới và ký tự abcdefghijklmnopqrstuvwxyz123456789(){},+-*/%;=.

Mã của bạn phải xuất số điểm mà bài tập của học sinh sẽ nhận được theo phiếu tự đánh giá sau:

  • Đầu vào không hợp lệ translation-unittheo ngữ pháp: 0 điểm
  • Đầu vào tuân theo ngữ pháp nhưng không được "hình thành tốt" theo các quy tắc dưới đây: 1 điểm
  • Đầu vào là một đơn vị dịch được hình thành tốt nhưng không đầy đủ mô-đun: 2 điểm
  • Đầu vào là một đơn vị dịch thuật được hình thành đầy đủ mô-đun: 3 điểm

Định nghĩa mã thông báo

  • identifier: Bất kỳ chuỗi từ 1 hoặc nhiều chữ cái tiếng Anh viết thường. Nếu số nhận dạng là từ dành riêng 1 C89 , bạn có thể tùy ý trả về 0 thay vì bất kỳ kết quả nào có thể đã bỏ qua các từ dành riêng. Bạn không nhất quán về việc phát hiện việc sử dụng các từ dành riêng làm định danh; bạn có thể gắn cờ chúng trong một số trường hợp và để chúng vượt qua trong những trường hợp khác.

  • integer-literal: Một chuỗi gồm 1 hoặc nhiều chữ số 1-9 (nhắc lại rằng ký tự 0được đảm bảo không xuất hiện trong đầu vào)

  • Các mã thông báo hợp lệ khác được định nghĩa theo nghĩa đen trong ngữ pháp.
  • Một ký tự phải thuộc về một mã thông báo khi và chỉ khi nó không phải là khoảng trắng.
  • Hai ký tự chữ và số liên tiếp phải là một phần của cùng một mã thông báo.

Ngữ pháp EBNF

var-expr = identifier
literal-expr = integer-literal
binary-op = "+" | "-" | "*" | "/" | "%"
binary-expr = expr binary-op expr
paren-expr = "(" expr ")"
call-expr = identifier "(" [ expr ( "," expr )* ] ")"
expr = var-expr | literal-expr | binary-expr | paren-expr | call-expr
assign-stmt = var-expr "=" expr ";"
if-stmt = "if" "(" expr ")" assign-stmt
return-stmt = "return" expr ";"
function-body = ( assign-stmt | if-stmt )* return-stmt
argument-list = [ identifier ( "," identifier )* ]
function-definition = identifier "(" argument-list ")" "{" function-body "}"
translation-unit = function-definition*

Yêu cầu chương trình tốt

  • Không có hai định nghĩa hàm có thể có cùng tên hàm.
  • Không có hai định danh trong một argument-listcó thể giống hệt nhau.
  • Không có định danh trong một argument-listcó thể giống hệt với tên hàm (cho dù từ a function-definitionhoặc a call-expr).
  • Mã định danh trong var-exprphải được bao gồm trong hàm kèm theo argument-list.
  • Đối với một hàm đã cho, tất cả call-exprs và function-definition(nếu có) phải đồng ý về số lượng đối số.

Mô-đun đầy đủ

  • Không quá 1 toán tử nhị phân cho mỗi hàm
  • Không quá 1 câu lệnh gán cho mỗi hàm
  • Không quá 1 chức năng gọi cho mỗi chức năng

Ví dụ (một trên mỗi dòng)

Điểm 0

}}}}}
return 2;
f() { return -1; }
f() {}
f(x,) { return 1; }
f(x) { return 1 }
f(x) { returnx; }
f(x) { return1; }
f() { g(); return 1;}
f() { if(1) return 5; }
f(x) { if(1) if(1) x = 2; return x; }
f(x, y) { x = y = 2; return x; }

Điểm 1

f(){ return 1; } f(){ return 1; }
g(x, x) { return 1; }
g(f) { return 1; } f() { return 1; }
f(x) { x = write(); x = write(1); return 1; }
f() { return f(f); }
f() { return 1; } g() { return f(234567); }
f() { return(x); }
f() { j = 7; return 5; }

Điểm 2

f(x,y,zzzzz) { return x + y + zzzzz; }
f(x,a,b) { if(a) x = foo(); if(b) x = bar(); return x; }
f(j) { return g(h( i() / j, i() ), 1) ; }

Điểm 3

mod(x, y) { return ((x % y)); }
f() { return f(); }
f(c) { if(c) c = g(c) + 2; return c; }
fib(i){return bb(i,0,1);}aa(i,a,b){return bb(i,b,a+b);}bb(i,a,b){if(i)a=aa(i-1,a,b);return a;}

Điểm 0 hoặc 1

h(auto, auto) { return 1; }

Điểm 0 hoặc 3

if() { return 1; }

1 danh sách từ dành riêng:auto break case char const continue default do double else enum extern float for goto if int long register return short signed sizeof static struct switch typedef union unsigned void volatile while


Chuỗi đầu vào có trống không? (Nếu vậy, tôi nghĩ rằng nó nên đạt điểm 3.)
Arnauld

@Arnauld Vâng, đúng vậy.
frageum

Làm thế nào chúng ta phải kiểm tra số lượng đối số của các hàm không được khai báo? Là ví dụ thứ 4 trong "Điểm 1" không được định dạng tốt vì writeđược gọi một lần không có đối số và một lần với một đối số?
Arnauld

@Arnauld Có, bạn kiểm tra xem mỗi cuộc gọi đến một hàm không được xác định có cùng số lượng đối số.
frageum

Tôi hiểu rồi. Chà, điều này hiện không tương thích với trình phân tích cú pháp của tôi, vì vậy bây giờ tôi đang xóa câu trả lời của mình. Tôi có thể thử lại sau.
Arnauld

Câu trả lời:


3

JavaScript (ES6), 599 byte

T=_=>P=I.shift()
B=v=>I.unshift(P)&&v
J=_=>/^[a-z]+$/.test(T())*7
M=p=>T()==p&&7
C=(n,a)=>n in U?U[n]==a?7:1:(U[n]=a,7)
E=(m,O=n=>E()&(M`,`?O(n+1):P==")"&&C(m,n)))=>(M`(`?E()&M`)`:P==+P?7:J(m=B(P))&(M`(`?++z&&M`)`?C(m,0):O(B(1)):B(A[m]|1)))&(/[+*/%-]/.test(T())?E(++y):B(7))
S=_=>I[0]?M`return`?E()&M`;`&M`}`&C(F,N)&((x|y|z)>1?3:7):(P=="if"?M`(`&E()&M`)`:B(7))&J(++x)&(A[P]|1)&M`=`&E()&M`;`&S():0
G=_=>J(++N)&(A[P]|L[P]&8?1:A[P]=L[P]=7)&(M`,`?G():P==")"&&M`{`&S())
Q=_=>I[N=x=y=z=0]?J(A={})&(L[F=P]?1:L[F]=15)&M`(`&(M`)`?M`{`&S():G(B()))&Q():7
f=c=>Math.log2(Q(U={},L={},I=c.match(/\w+|\S/g)||[])+1)

Hãy thử trực tuyến! (đi kèm với các trường hợp thử nghiệm)

Xác định hàm flấy mã làm đối số.

(Một số) giải thích

Sự quái dị này về cơ bản là một bit đệ quy rất lớn AND, bằng cách nào đó tình cờ hoạt động như một trình phân tích cú pháp cho tập hợp con C này. Điểm được lưu trữ như 0137cho 0123tương ứng và chuyển đổi trong khi kết thúc như log2(score+1); nếu bất kỳ pha nào phát hiện ra một vấn đề trong mã, nó sẽ trả về điểm thấp hơn 7, do đó sẽ bỏ các bit từ đầu ra cuối cùng và dẫn đến điểm thấp hơn. Tất cả các chức năng ngoại trừ TBtrả lại một số điểm.

Các định danh được sử dụng được theo dõi Lvà tính đối số hàm trong U. Số lượng đối số của hàm hiện tại là Nvà các tên nằm trong A. Bài tập, các nhà khai thác nhị phân và các cuộc gọi được theo dõi trong x, yztương ứng.

Nếu mã hết đầu vào, nó sẽ tiếp tục vui vẻ bật undefinedra khỏi mã thông báo mã thông báo. Điều này là tốt, vì chức năng duy nhất phù hợp undefinedJ, và cuối cùng mọi thứ sẽ kết thúc đệ quy và trả về 0 do không khớp với đóng }. (Ngoại trừ SQcó kiểm tra rõ ràng về kết thúc đầu vào.) Ngay cả việc đẩy undefinedlùi deque cũng vô hại, vì popping undefinedcũng giống như popping một mảng trống.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.