打印本文 打印本文 关闭窗口 关闭窗口
Why Pascal is Not My Favourite Programming Language
作者:武汉SEO闵涛  文章来源:敏韬网  点击数5009  更新时间:2009/4/23 18:38:49  文章录入:mintao  责任编辑:mintao
''] end; But in many implementations of Pascal (including the original) this code fails because sets are just too small. Accordingly, sets are generally best left unused if one intends to write portable pro grams. (This specific routine also runs an order of magnitude slower with sets than with a range test or array reference.) 2.6. There is no escape There is no way to override the type mechanism when necessary, nothing analogous to the ``cast'''''''' mechanism in C. This means that it is not possible to write programs like storage alloca tors or I/O systems in Pascal, because there is no way to talk about the type of object that they return, and no way to force such objects into an arbitrary type for another use. (Strictly speaking, there is a large hole in the type-checking near variant records, through which some otherwise illegal type mismatches can be obtained.) 3. Control Flow The control flow deficiencies of Pascal are minor but numerous -- the death of a thousand cuts, rather than a single blow to a vital spot. There is no guaranteed order of evaluation of the logical operators and and or -- nothing like && and || in C. This failing, which is shared with most other languages, hurts most often in loop control: while (i <= XMAX) and (x[i] > 0) do ... is extremely unwise Pascal usage, since there is no way to ensure that i is tested before x[i] is. By the way, the parentheses in this code are mandatory -- the language has only four levels of operator precedence, with relationals at the bottom. There is no break statement for exiting loops. This is consistent with the one entry-one exit philosophy espoused by proponents of structured programming, but it does lead to nasty cir cumlocutions or duplicated code, particularly when coupled with the inability to control the order in which logical expressions are evaluated. Consider this common situation, expressed in C or Ratfor: while (getnext(...)) { if (something) break rest of loop } With no break statement, the first attempt in Pascal is done := false; while (not done) and (getnext(...)) do if something then done := true else begin rest of loop end But this doesn''''t work, because there is no way to force the ``not done'''''''' to be evaluated before the next call of getnext. This leads, after several false starts, to done := false; while not done do begin done := getnext(...); if something then done := true else if not done then begin rest of loop end end Of course recidivists can use a goto and a label (numeric only and it has to be declared) to exit a loop. Otherwise, early exits are a pain, almost always requiring the invention of a boolean vari able and a certain amount of cunning. Compare finding the last non-blank in an array in Ratfor: for (i = max; i > 0; i = i - 1) if (arr(i) != '''' '''') break with Pascal: done := false; i := max; while (i > 0) and (not done) do if arr[i] = '''' '''' then i := i - 1 else done := true; The index of a for loop is undefined outside the loop, so it is not possible to figure out whether one went to the end or not. The increment of a for loop can only be +1 or -1, a minor restriction. There is no return statement, again for one in-one out reasons. A function value is returned by setting the value of a pseudo-variable (as in Fortran), then falling off the end of the function. This sometimes leads to contortions to make sure that all paths actually get to the end of the function with the proper value. There is also no standard way to terminate execution except by reaching the end of the outermost block, although many implementations provide a halt that causes immediate termination. The case statement is better designed than in C, except that there is no default clause and the behavior is undefined if the input expression does not match any of the cases. This crucial omission renders the case construct almost worthless. In over 6000 lines of Pascal in Software Tools in Pascal, I used it only four times, although if there had been a default, a case would have served in at least a dozen places. The new standard offers no relief on any of these points. 4. The Environment The Pascal run-time environment is relatively sparse, and there is no extension mechanism except perhaps source-level libraries in the ``official'''''''' language. Pascal''''s built-in I/O has a deservedly bad reputation. It believes strongly in record oriented input and output. It also has a look-ahead convention that is hard to implement prop erly in an interactive environment. Basically, the problem is that the I/O system believes that it must read one record ahead of the record that is being processed. In an interactive system, this means that when a program is started, its first operation is to try to read the terminal for the first line of input, before any of the program itself has been executed. But in the program write(''''Please enter your name: ''''); read(name); ... read-ahead causes the program to hang, waiting for input before printing the prompt that asks for it. It is possible to escape most of the evil effects of this I/O design by very careful implemen tation, but not all Pascal systems do so, and in any case it is relatively costly. The I/O design reflects the original operating system upon which Pascal was designed; even Wirth acknowledges that bias, though not its defects. 15 It is assumed that text files consist of records, that is, lines of text. When the last character of a line is read, the built-in function eoln becomes true; at that point, one must call readln to initiate reading a new line and reset eoln. Similarly, when the last character of the file is read, the built-in eof becomes true. In both cases, eoln and eof must be tested before each read rather than after. Given this, considerable pains must be taken to simulate sensible input. This implementa tion of getc works for Berkeley and VU I/O systems, but may not necessarily work for anything else: { getc -- read character from standard input } function getc (var c : character) : character; var ch : char; begin if eof then c := ENDFILE else if eoln then begin readln; c := NEWLINE end else begin read(ch); c := ord(ch) end; getc := c end; The type character is not the same as char, since ENDFILE and perhaps NEWLINE are not legal values for a char variable. There is no notion at all of access to a file system except for predefined files named by (in effect) logical unit number in the program statement that begins each program. This apparently reflects the CDC batch system in which Pascal was originally developed. A file variable var fv : file of type is a very special kind of object -- it cannot be assigned to, nor used except by calls to built-in pro cedures like eof, eoln, read, write, reset and rewrite. (reset rewinds a file and makes it ready for re-reading; rewrite makes a file ready for writing.) Most implementations of Pascal provide an escape hatch to allow access to files by name from the outside environment, but not conveniently and not standardly. For example, many sys tems permit a filename argument in calls to reset and rewrite: reset(fv, filename); But reset and rewrite are procedures, not functions -- there is no status return and no way to regain control if for some reason the attempted access fails. (UCSD provides a compile-time flag that disables the normal abort.) And since fv''''s cannot appear in expressions like reset(fv, filename); if fv = failure then ... there is no escape in that direction either. This straitjacket makes it essentially impossible to write programs that recover from mis-spelled file names, etc. I never solved it adequately in the Tools revision. There is no notion of access to command-line arguments, again probably reflecting Pascal''''s batch-processing origins. Local routines may allow it by adding non-standard procedures to the environment. Since it is not possible to write a general-purpose storage allocator in Pascal (there being no way to talk about the types that such a function would return), the language has a built-in proce dure called new that allocates space from a heap. Only defined types may be allocated, so it is not possible to allocate, for example, arrays of arbitrary size to hold character strings. The point ers returned by new may be passed around but not manipulated: there is no pointer arithmetic. There is no way to regain control if storage runs out. The new standard offers no change in any of these areas. 5. Cosmetic Issues Most of these issues are irksome to an experienced programmer, and some are probably a nuisance even to beginners. All can be lived with. Pascal, in common with most other Algol-inspired languages, uses the semicolon as a state ment separator rather than a terminator (as it is in PL/I and C). As a result one must have a rea sonably sophisticated notion of what a statement is to put semicolons in properly. Perhaps more important, if one is serious about using them in the proper places, a fair amount of nuisance edit ing is needed. Consider the first cut at a program: if a then b; c; But if something must be inserted before b, it no longer needs a semicolon, because it now pre cedes an end: if a then begin b0; b end; c; Now if we add an else, we must remove the semicolon on the end: if a then begin b0; b end else d; c; And so on and so on, with semicolons rippling up and down the program as it evolves. One generally accepted experimental result in programmer psychology is that semicolon as separator is about ten times more prone to error than semicolon as terminator. 16 (In Ada, 17 the most significant language based on Pascal, semicolon is a terminator.) Fortunately, in Pascal one can almost always close one''''s eyes and get away with a semicolon as a terminator. The excep tions are in places like declarations, where the separator vs. terminator problem doesn''''t seem as serio

上一页  [1] [2] [3] [4] [5]  下一页

打印本文 打印本文 关闭窗口 关闭窗口