About neohope

一直在努力,还没想过要放弃...

The Humble Programmer


The Humble Programmer
by Edsger W. Dijkstra

As a result of a long sequence of coincidences I entered the programming profession officially on the first spring morning of 1952 and as far as I have been able to trace, I was the first Dutchman to do so in my country. In retrospect the most amazing thing was the slowness with which, at least in my part of the world, the programming profession emerged, a slowness which is now hard to believe. But I am grateful for two vivid recollections from that period that establish that slowness beyond any doubt.

After having programmed for some three years, I had a discussion with A. van Wijngaarden, who was then my boss at the Mathematical Centre in Amsterdam, a discussion for which I shall remain grateful to him as long as I live. The point was that I was supposed to study theoretical physics at the University of Leiden simultaneously, and as I found the two activities harder and harder to combine, I had to make up my mind, either to stop programming and become a real, respectable theoretical physicist, or to carry my study of physics to a formal completion only, with a minimum of effort, and to become….., yes what? A programmer? But was that a respectable profession? For after all, what was programming? Where was the sound body of knowledge that could support it as an intellectually respectable discipline? I remember quite vividly how I envied my hardware colleagues, who, when asked about their professional competence, could at least point out that they knew everything about vacuum tubes, amplifiers and the rest, whereas I felt that, when faced with that question, I would stand empty-handed. Full of misgivings I knocked on van Wijngaarden’s office door, asking him whether I could “speak to him for a moment”; when I left his office a number of hours later, I was another person. For after having listened to my problems patiently, he agreed that up till that moment there was not much of a programming discipline, but then he went on to explain quietly that automatic computers were here to stay, that we were just at the beginning and could not I be one of the persons called to make programming a respectable discipline in the years to come? This was a turning point in my life and I completed my study of physics formally as quickly as I could. One moral of the above story is, of course, that we must be very careful when we give advice to younger people; sometimes they follow it!

Another two years later, in 1957, I married and Dutch marriage rites require you to state your profession and I stated that I was a programmer. But the municipal authorities of the town of Amsterdam did not accept it on the grounds that there was no such profession. And, believe it or not, but under the heading “profession” my marriage act shows the ridiculous entry “theoretical physicist”!

So much for the slowness with which I saw the programming profession emerge in my own country. Since then I have seen more of the world, and it is my general impression that in other countries, apart from a possible shift of dates, the growth pattern has been very much the same.

Let me try to capture the situation in those old days in a little bit more detail, in the hope of getting a better understanding of the situation today. While we pursue our analysis, we shall see how many common misunderstandings about the true nature of the programming task can be traced back to that now distant past.

The first automatic electronic computers were all unique, single-copy machines and they were all to be found in an environment with the exciting flavour of an experimental laboratory. Once the vision of the automatic computer was there, its realisation was a tremendous challenge to the electronic technology then available, and one thing is certain: we cannot deny the courage of the groups that decided to try and build such a fantastic piece of equipment. For fantastic pieces of equipment they were: in retrospect one can only wonder that those first machines worked at all, at least sometimes. The overwhelming problem was to get and keep the machine in working order. The preoccupation with the physical aspects of automatic computing is still reflected in the names of the older scientific societies in the field, such as the Association for Computing Machinery or the British Computer Society, names in which explicit reference is made to the physical equipment.

What about the poor programmer? Well, to tell the honest truth: he was hardly noticed. For one thing, the first machines were so bulky that you could hardly move them and besides that, they required such extensive maintenance that it was quite natural that the place where people tried to use the machine was the same laboratory where the machine had been developed. Secondly, his somewhat invisible work was without any glamour: you could show the machine to visitors and that was several orders of magnitude more spectacular than some sheets of coding. But most important of all, the programmer himself had a very modest view of his own work: his work derived all its significance from the existence of that wonderful machine. Because that was a unique machine, he knew only too well that his programs had only local significance and also, because it was patently obvious that this machine would have a limited lifetime, he knew that very little of his work would have a lasting value. Finally, there is yet another circumstance that had a profound influence on the programmer’s attitude to his work: on the one hand, besides being unreliable, his machine was usually too slow and its memory was usually too small, i.e. he was faced with a pinching shoe, while on the other hand its usually somewhat queer order code would cater for the most unexpected constructions. And in those days many a clever programmer derived an immense intellectual satisfaction from the cunning tricks by means of which he contrived to squeeze the impossible into the constraints of his equipment.

Two opinions about programming date from those days. I mention them now, I shall return to them later. The one opinion was that a really competent programmer should be puzzle-minded and very fond of clever tricks; the other opinion was that programming was nothing more than optimizing the efficiency of the computational process, in one direction or the other.

The latter opinion was the result of the frequent circumstance that, indeed, the available equipment was a painfully pinching shoe, and in those days one often encountered the naive expectation that, once more powerful machines were available, programming would no longer be a problem, for then the struggle to push the machine to its limits would no longer be necessary and that was all what programming was about, wasn’t it? But in the next decades something completely different happened: more powerful machines became available, not just an order of magnitude more powerful, even several orders of magnitude more powerful. But instead of finding ourselves in the state of eternal bliss of all programming problems solved, we found ourselves up to our necks in the software crisis! How come?

There is a minor cause: in one or two respects modern machinery is basically more difficult to handle than the old machinery. Firstly, we have got the I/O interrupts, occurring at unpredictable and irreproducible moments; compared with the old sequential machine that pretended to be a fully deterministic automaton, this has been a dramatic change and many a systems programmer’s grey hair bears witness to the fact that we should not talk lightly about the logical problems created by that feature. Secondly, we have got machines equipped with multi-level stores, presenting us problems of management strategy that, in spite of the extensive literature on the subject, still remain rather elusive. So much for the added complication due to structural changes of the actual machines.

But I called this a minor cause; the major cause is… that the machines have become several orders of magnitude more powerful! To put it quite bluntly: as long as there were no machines, programming was no problem at all; when we had a few weak computers, programming became a mild problem, and now we have gigantic computers, programming had become an equally gigantic problem. In this sense the electronic industry has not solved a single problem, it has only created them, it has created the problem of using its products. To put it in another way: as the power of available machines grew by a factor of more than a thousand, society’s ambition to apply these machines grew in proportion, and it was the poor programmer who found his job in this exploded field of tension between ends and means. The increased power of the hardware, together with the perhaps even more dramatic increase in its reliability, made solutions feasible that the programmer had not dared to dream about a few years before. And now, a few years later, he had to dream about them and, even worse, he had to transform such dreams into reality! Is it a wonder that we found ourselves in a software crisis? No, certainly not, and as you may guess, it was even predicted well in advance; but the trouble with minor prophets, of course, is that it is only five years later that you really know that they had been right.

Then, in the mid-sixties, something terrible happened: the computers of the so-called third generation made their appearance. The official literature tells us that their price/performance ratio has been one of the major design objectives. But if you take as “performance” the duty cycle of the machine’s various components, little will prevent you from ending up with a design in which the major part of your performance goal is reached by internal housekeeping activities of doubtful necessity. And if your definition of price is the price to be paid for the hardware, little will prevent you from ending up with a design that is terribly hard to program for: for instance the order code might be such as to enforce, either upon the programmer or upon the system, early binding decisions presenting conflicts that really cannot be resolved. And to a large extent these unpleasant possibilities seem to have become reality.

When these machines were announced and their functional specifications became known, quite a few among us must have become quite miserable; at least I was. It was only reasonable to expect that such machines would flood the computing community, and it was therefore all the more important that their design should be as sound as possible. But the design embodied such serious flaws that I felt that with a single stroke the progress of computing science had been retarded by at least ten years: it was then that I had the blackest week in the whole of my professional life. Perhaps the most saddening thing now is that, even after all those years of frustrating experience, still so many people honestly believe that some law of nature tells us that machines have to be that way. They silence their doubts by observing how many of these machines have been sold, and derive from that observation the false sense of security that, after all, the design cannot have been that bad. But upon closer inspection, that line of defense has the same convincing strength as the argument that cigarette smoking must be healthy because so many people do it.

It is in this connection that I regret that it is not customary for scientific journals in the computing area to publish reviews of newly announced computers in much the same way as we review scientific publications: to review machines would be at least as important. And here I have a confession to make: in the early sixties I wrote such a review with the intention of submitting it to the CACM, but in spite of the fact that the few colleagues to whom the text was sent for their advice, urged me all to do so, I did not dare to do it, fearing that the difficulties either for myself or for the editorial board would prove to be too great. This suppression was an act of cowardice on my side for which I blame myself more and more. The difficulties I foresaw were a consequence of the absence of generally accepted criteria, and although I was convinced of the validity of the criteria I had chosen to apply, I feared that my review would be refused or discarded as “a matter of personal taste”. I still think that such reviews would be extremely useful and I am longing to see them appear, for their accepted appearance would be a sure sign of maturity of the computing community.

The reason that I have paid the above attention to the hardware scene is because I have the feeling that one of the most important aspects of any computing tool is its influence on the thinking habits of those that try to use it, and because I have reasons to believe that that influence is many times stronger than is commonly assumed. Let us now switch our attention to the software scene.

Here the diversity has been so large that I must confine myself to a few stepping stones. I am painfully aware of the arbitrariness of my choice and I beg you not to draw any conclusions with regard to my appreciation of the many efforts that will remain unmentioned.

In the beginning there was the EDSAC in Cambridge, England, and I think it quite impressive that right from the start the notion of a subroutine library played a central role in the design of that machine and of the way in which it should be used. It is now nearly 25 years later and the computing scene has changed dramatically, but the notion of basic software is still with us, and the notion of the closed subroutine is still one of the key concepts in programming. We should recognise the closed subroutines as one of the greatest software inventions; it has survived three generations of computers and it will survive a few more, because it caters for the implementation of one of our basic patterns of abstraction. Regrettably enough, its importance has been underestimated in the design of the third generation computers, in which the great number of explicitly named registers of the arithmetic unit implies a large overhead on the subroutine mechanism. But even that did not kill the concept of the subroutine, and we can only pray that the mutation won’t prove to be hereditary.

The second major development on the software scene that I would like to mention is the birth of FORTRAN. At that time this was a project of great temerity and the people responsible for it deserve our great admiration. It would be absolutely unfair to blame them for shortcomings that only became apparent after a decade or so of extensive usage: groups with a successful look-ahead of ten years are quite rare! In retrospect we must rate FORTRAN as a successful coding technique, but with very few effective aids to conception, aids which are now so urgently needed that time has come to consider it out of date. The sooner we can forget that FORTRAN has ever existed, the better, for as a vehicle of thought it is no longer adequate: it wastes our brainpower, is too risky and therefore too expensive to use. FORTRAN’s tragic fate has been its wide acceptance, mentally chaining thousands and thousands of programmers to our past mistakes. I pray daily that more of my fellow-programmers may find the means of freeing themselves from the curse of compatibility.

The third project I would not like to leave unmentioned is LISP, a fascinating enterprise of a completely different nature. With a few very basic principles at its foundation, it has shown a remarkable stability. Besides that, LISP has been the carrier for a considerable number of in a sense our most sophisticated computer applications. LISP has jokingly been described as “the most intelligent way to misuse a computer”. I think that description a great compliment because it transmits the full flavour of liberation: it has assisted a number of our most gifted fellow humans in thinking previously impossible thoughts.

The fourth project to be mentioned is ALGOL 60. While up to the present day FORTRAN programmers still tend to understand their programming language in terms of the specific implementation they are working with —hence the prevalence of octal and hexadecimal dumps—, while the definition of LISP is still a curious mixture of what the language means and how the mechanism works, the famous Report on the Algorithmic Language ALGOL 60 is the fruit of a genuine effort to carry abstraction a vital step further and to define a programming language in an implementation-independent way. One could argue that in this respect its authors have been so successful that they have created serious doubts as to whether it could be implemented at all! The report gloriously demonstrated the power of the formal method BNF, now fairly known as Backus-Naur-Form, and the power of carefully phrased English, a least when used by someone as brilliant as Peter Naur. I think that it is fair to say that only very few documents as short as this have had an equally profound influence on the computing community. The ease with which in later years the names ALGOL and ALGOL-like have been used, as an unprotected trade mark, to lend some of its glory to a number of sometimes hardly related younger projects, is a somewhat shocking compliment to its standing. The strength of BNF as a defining device is responsible for what I regard as one of the weaknesses of the language: an over-elaborate and not too systematic syntax could now be crammed into the confines of very few pages. With a device as powerful as BNF, the Report on the Algorithmic Language ALGOL 60 should have been much shorter. Besides that I am getting very doubtful about ALGOL 60’s parameter mechanism: it allows the programmer so much combinatorial freedom, that its confident use requires a strong discipline from the programmer. Besides expensive to implement it seems dangerous to use.

Finally, although the subject is not a pleasant one, I must mention PL/1, a programming language for which the defining documentation is of a frightening size and complexity. Using PL/1 must be like flying a plane with 7000 buttons, switches and handles to manipulate in the cockpit. I absolutely fail to see how we can keep our growing programs firmly within our intellectual grip when by its sheer baroqueness the programming language —our basic tool, mind you!— already escapes our intellectual control. And if I have to describe the influence PL/1 can have on its users, the closest metaphor that comes to my mind is that of a drug. I remember from a symposium on higher level programming language a lecture given in defense of PL/1 by a man who described himself as one of its devoted users. But within a one-hour lecture in praise of PL/1. he managed to ask for the addition of about fifty new “features”, little supposing that the main source of his problems could very well be that it contained already far too many “features”. The speaker displayed all the depressing symptoms of addiction, reduced as he was to the state of mental stagnation in which he could only ask for more, more, more… When FORTRAN has been called an infantile disorder, full PL/1, with its growth characteristics of a dangerous tumor, could turn out to be a fatal disease.

So much for the past. But there is no point in making mistakes unless thereafter we are able to learn from them. As a matter of fact, I think that we have learned so much, that within a few years programming can be an activity vastly different from what it has been up till now, so different that we had better prepare ourselves for the shock. Let me sketch for you one of the possible futures. At first sight, this vision of programming in perhaps already the near future may strike you as utterly fantastic. Let me therefore also add the considerations that might lead one to the conclusion that this vision could be a very real possibility.

The vision is that, well before the seventies have run to completion, we shall be able to design and implement the kind of systems that are now straining our programming ability, at the expense of only a few percent in man-years of what they cost us now, and that besides that, these systems will be virtually free of bugs. These two improvements go hand in hand. In the latter respect software seems to be different from many other products, where as a rule a higher quality implies a higher price. Those who want really reliable software will discover that they must find means of avoiding the majority of bugs to start with, and as a result the programming process will become cheaper. If you want more effective programmers, you will discover that they should not waste their time debugging, they should not introduce the bugs to start with. In other words: both goals point to the same change.

Such a drastic change in such a short period of time would be a revolution, and to all persons that base their expectations for the future on smooth extrapolation of the recent past —appealing to some unwritten laws of social and cultural inertia— the chance that this drastic change will take place must seem negligible. But we all know that sometimes revolutions do take place! And what are the chances for this one?

There seem to be three major conditions that must be fulfilled. The world at large must recognize the need for the change; secondly the economic need for it must be sufficiently strong; and, thirdly, the change must be technically feasible. Let me discuss these three conditions in the above order.

With respect to the recognition of the need for greater reliability of software, I expect no disagreement anymore. Only a few years ago this was different: to talk about a software crisis was blasphemy. The turning point was the Conference on Software Engineering in Garmisch, October 1968, a conference that created a sensation as there occurred the first open admission of the software crisis. And by now it is generally recognized that the design of any large sophisticated system is going to be a very difficult job, and whenever one meets people responsible for such undertakings, one finds them very much concerned about the reliability issue, and rightly so. In short, our first condition seems to be satisfied.

Now for the economic need. Nowadays one often encounters the opinion that in the sixties programming has been an overpaid profession, and that in the coming years programmer salaries may be expected to go down. Usually this opinion is expressed in connection with the recession, but it could be a symptom of something different and quite healthy, viz. that perhaps the programmers of the past decade have not done so good a job as they should have done. Society is getting dissatisfied with the performance of programmers and of their products. But there is another factor of much greater weight. In the present situation it is quite usual that for a specific system, the price to be paid for the development of the software is of the same order of magnitude as the price of the hardware needed, and society more or less accepts that. But hardware manufacturers tell us that in the next decade hardware prices can be expected to drop with a factor of ten. If software development were to continue to be the same clumsy and expensive process as it is now, things would get completely out of balance. You cannot expect society to accept this, and therefore we must learn to program an order of magnitude more effectively. To put it in another way: as long as machines were the largest item on the budget, the programming profession could get away with its clumsy techniques, but that umbrella will fold rapidly. In short, also our second condition seems to be satisfied.

And now the third condition: is it technically feasible? I think it might and I shall give you six arguments in support of that opinion.

A study of program structure had revealed that programs —even alternative programs for the same task and with the same mathematical content— can differ tremendously in their intellectual manageability. A number of rules have been discovered, violation of which will either seriously impair or totally destroy the intellectual manageability of the program. These rules are of two kinds. Those of the first kind are easily imposed mechanically, viz. by a suitably chosen programming language. Examples are the exclusion of goto-statements and of procedures with more than one output parameter. For those of the second kind I at least —but that may be due to lack of competence on my side— see no way of imposing them mechanically, as it seems to need some sort of automatic theorem prover for which I have no existence proof. Therefore, for the time being and perhaps forever, the rules of the second kind present themselves as elements of discipline required from the programmer. Some of the rules I have in mind are so clear that they can be taught and that there never needs to be an argument as to whether a given program violates them or not. Examples are the requirements that no loop should be written down without providing a proof for termination nor without stating the relation whose invariance will not be destroyed by the execution of the repeatable statement.

I now suggest that we confine ourselves to the design and implementation of intellectually manageable programs. If someone fears that this restriction is so severe that we cannot live with it, I can reassure him: the class of intellectually manageable programs is still sufficiently rich to contain many very realistic programs for any problem capable of algorithmic solution. We must not forget that it is not our business to make programs, it is our business to design classes of computations that will display a desired behaviour. The suggestion of confining ourselves to intellectually manageable programs is the basis for the first two of my announced six arguments.

Argument one is that, as the programmer only needs to consider intellectually manageable programs, the alternatives he is choosing between are much, much easier to cope with.

Argument two is that, as soon as we have decided to restrict ourselves to the subset of the intellectually manageable programs, we have achieved, once and for all, a drastic reduction of the solution space to be considered. And this argument is distinct from argument one.

Argument three is based on the constructive approach to the problem of program correctness. Today a usual technique is to make a program and then to test it. But: program testing can be a very effective way to show the presence of bugs, but is hopelessly inadequate for showing their absence. The only effective way to raise the confidence level of a program significantly is to give a convincing proof of its correctness. But one should not first make the program and then prove its correctness, because then the requirement of providing the proof would only increase the poor programmer’s burden. On the contrary: the programmer should let correctness proof and program grow hand in hand. Argument three is essentially based on the following observation. If one first asks oneself what the structure of a convincing proof would be and, having found this, then constructs a program satisfying this proof’s requirements, then these correctness concerns turn out to be a very effective heuristic guidance. By definition this approach is only applicable when we restrict ourselves to intellectually manageable programs, but it provides us with effective means for finding a satisfactory one among these.

Argument four has to do with the way in which the amount of intellectual effort needed to design a program depends on the program length. It has been suggested that there is some kind of law of nature telling us that the amount of intellectual effort needed grows with the square of program length. But, thank goodness, no one has been able to prove this law. And this is because it need not be true. We all know that the only mental tool by means of which a very finite piece of reasoning can cover a myriad cases is called “abstraction”; as a result the effective exploitation of his powers of abstraction must be regarded as one of the most vital activities of a competent programmer. In this connection it might be worth-while to point out that the purpose of abstracting is not to be vague, but to create a new semantic level in which one can be absolutely precise. Of course I have tried to find a fundamental cause that would prevent our abstraction mechanisms from being sufficiently effective. But no matter how hard I tried, I did not find such a cause. As a result I tend to the assumption —up till now not disproved by experience— that by suitable application of our powers of abstraction, the intellectual effort needed to conceive or to understand a program need not grow more than proportional to program length. But a by-product of these investigations may be of much greater practical significance, and is, in fact, the basis of my fourth argument. The by-product was the identification of a number of patterns of abstraction that play a vital role in the whole process of composing programs. Enough is now known about these patterns of abstraction that you could devote a lecture to about each of them. What the familiarity and conscious knowledge of these patterns of abstraction imply dawned upon me when I realized that, had they been common knowledge fifteen years ago, the step from BNF to syntax-directed compilers, for instance, could have taken a few minutes instead of a few years. Therefore I present our recent knowledge of vital abstraction patterns as the fourth argument.

Now for the fifth argument. It has to do with the influence of the tool we are trying to use upon our own thinking habits. I observe a cultural tradition, which in all probability has its roots in the Renaissance, to ignore this influence, to regard the human mind as the supreme and autonomous master of its artefacts. But if I start to analyse the thinking habits of myself and of my fellow human beings, I come, whether I like it or not, to a completely different conclusion, viz. that the tools we are trying to use and the language or notation we are using to express or record our thoughts, are the major factors determining what we can think or express at all! The analysis of the influence that programming languages have on the thinking habits of its users, and the recognition that, by now, brainpower is by far our scarcest resource, they together give us a new collection of yardsticks for comparing the relative merits of various programming languages. The competent programmer is fully aware of the strictly limited size of his own skull; therefore he approaches the programming task in full humility, and among other things he avoids clever tricks like the plague. In the case of a well-known conversational programming language I have been told from various sides that as soon as a programming community is equipped with a terminal for it, a specific phenomenon occurs that even has a well-established name: it is called “the one-liners”. It takes one of two different forms: one programmer places a one-line program on the desk of another and either he proudly tells what it does and adds the question “Can you code this in less symbols?” —as if this were of any conceptual relevance!— or he just asks “Guess what it does!”. From this observation we must conclude that this language as a tool is an open invitation for clever tricks; and while exactly this may be the explanation for some of its appeal, viz. to those who like to show how clever they are, I am sorry, but I must regard this as one of the most damning things that can be said about a programming language. Another lesson we should have learned from the recent past is that the development of “richer” or “more powerful” programming languages was a mistake in the sense that these baroque monstrosities, these conglomerations of idiosyncrasies, are really unmanageable, both mechanically and mentally. I see a great future for very systematic and very modest programming languages. When I say “modest”, I mean that, for instance, not only ALGOL 60’s “for clause”, but even FORTRAN’s “DO loop” may find themselves thrown out as being too baroque. I have run a a little programming experiment with really experienced volunteers, but something quite unintended and quite unexpected turned up. None of my volunteers found the obvious and most elegant solution. Upon closer analysis this turned out to have a common source: their notion of repetition was so tightly connected to the idea of an associated controlled variable to be stepped up, that they were mentally blocked from seeing the obvious. Their solutions were less efficient, needlessly hard to understand, and it took them a very long time to find them. It was a revealing, but also shocking experience for me. Finally, in one respect one hopes that tomorrow’s programming languages will differ greatly from what we are used to now: to a much greater extent than hitherto they should invite us to reflect in the structure of what we write down all abstractions needed to cope conceptually with the complexity of what we are designing. So much for the greater adequacy of our future tools, which was the basis of the fifth argument.

As an aside I would like to insert a warning to those who identify the difficulty of the programming task with the struggle against the inadequacies of our current tools, because they might conclude that, once our tools will be much more adequate, programming will no longer be a problem. Programming will remain very difficult, because once we have freed ourselves from the circumstantial cumbersomeness, we will find ourselves free to tackle the problems that are now well beyond our programming capacity.

You can quarrel with my sixth argument, for it is not so easy to collect experimental evidence for its support, a fact that will not prevent me from believing in its validity. Up till now I have not mentioned the word “hierarchy”, but I think that it is fair to say that this is a key concept for all systems embodying a nicely factored solution. I could even go one step further and make an article of faith out of it, viz. that the only problems we can really solve in a satisfactory manner are those that finally admit a nicely factored solution. At first sight this view of human limitations may strike you as a rather depressing view of our predicament, but I don’t feel it that way, on the contrary! The best way to learn to live with our limitations is to know them. By the time that we are sufficiently modest to try factored solutions only, because the other efforts escape our intellectual grip, we shall do our utmost best to avoid all those interfaces impairing our ability to factor the system in a helpful way. And I cannot but expect that this will repeatedly lead to the discovery that an initially untractable problem can be factored after all. Anyone who has seen how the majority of the troubles of the compiling phase called “code generation” can be tracked down to funny properties of the order code, will know a simple example of the kind of things I have in mind. The wider applicability of nicely factored solutions is my sixth and last argument for the technical feasibility of the revolution that might take place in the current decade.

In principle I leave it to you to decide for yourself how much weight you are going to give to my considerations, knowing only too well that I can force no one else to share my beliefs. As each serious revolution, it will provoke violent opposition and one can ask oneself where to expect the conservative forces trying to counteract such a development. I don’t expect them primarily in big business, not even in the computer business; I expect them rather in the educational institutions that provide today’s training and in those conservative groups of computer users that think their old programs so important that they don’t think it worth-while to rewrite and improve them. In this connection it is sad to observe that on many a university campus the choice of the central computing facility has too often been determined by the demands of a few established but expensive applications with a disregard of the question how many thousands of “small users” that are willing to write their own programs were going to suffer from this choice. Too often, for instance, high-energy physics seems to have blackmailed the scientific community with the price of its remaining experimental equipment. The easiest answer, of course, is a flat denial of the technical feasibility, but I am afraid that you need pretty strong arguments for that. No reassurance, alas, can be obtained from the remark that the intellectual ceiling of today’s average programmer will prevent the revolution from taking place: with others programming so much more effectively, he is liable to be edged out of the picture anyway.

There may also be political impediments. Even if we know how to educate tomorrow’s professional programmer, it is not certain that the society we are living in will allow us to do so. The first effect of teaching a methodology —rather than disseminating knowledge— is that of enhancing the capacities of the already capable, thus magnifying the difference in intelligence. In a society in which the educational system is used as an instrument for the establishment of a homogenized culture, in which the cream is prevented from rising to the top, the education of competent programmers could be politically impalatable.

Let me conclude. Automatic computers have now been with us for a quarter of a century. They have had a great impact on our society in their capacity of tools, but in that capacity their influence will be but a ripple on the surface of our culture, compared with the much more profound influence they will have in their capacity of intellectual challenge without precedent in the cultural history of mankind. Hierarchical systems seem to have the property that something considered as an undivided entity on one level, is considered as a composite object on the next lower level of greater detail; as a result the natural grain of space or time that is applicable at each level decreases by an order of magnitude when we shift our attention from one level to the next lower one. We understand walls in terms of bricks, bricks in terms of crystals, crystals in terms of molecules etc. As a result the number of levels that can be distinguished meaningfully in a hierarchical system is kind of proportional to the logarithm of the ratio between the largest and the smallest grain, and therefore, unless this ratio is very large, we cannot expect many levels. In computer programming our basic building block has an associated time grain of less than a microsecond, but our program may take hours of computation time. I do not know of any other technology covering a ratio of 1010 or more: the computer, by virtue of its fantastic speed, seems to be the first to provide us with an environment where highly hierarchical artefacts are both possible and necessary. This challenge, viz. the confrontation with the programming task, is so unique that this novel experience can teach us a lot about ourselves. It should deepen our understanding of the processes of design and creation, it should give us better control over the task of organizing our thoughts. If it did not do so, to my taste we should not deserve the computer at all!

It has already taught us a few lessons, and the one I have chosen to stress in this talk is the following. We shall do a much better programming job, provided that we approach the task with a full appreciation of its tremendous difficulty, provided that we stick to modest and elegant programming languages, provided that we respect the intrinsic limitations of the human mind and approach the task as Very Humble Programmers.

原文:原文地址

谦逊的程序员


谦逊的程序员
Edsger W. Dijkstra
黄索远(译)

1952年初春的早晨,由于一系列巧合,我正式进入了编程界,成为了程序员历史上第一位荷兰人。回想起来,程序员作为一种职业,是以现在很难相信的缓慢速度出现的,至少在我所了解的范围内是这样,这让人非常惊奇。那个缓慢时期的两段清晰回忆让我心存感激。

在有了三年编程经验后,我和当时我在阿姆斯特丹数学中心的老板 A. Van Wijngaarden(译注:荷兰数学家和计算机科学家),进行了一场讨论,关于这场讨论我在有生之年都会感激他。讨论的焦点在于,我本来是要在编程的同时,在莱顿大学学习理论物理,但是当发现鱼和熊掌不可兼得时,我不得不做出选择,要么停止编程,成为一个名符其实的可敬的理论物理学家;或者把我的物理学研究敷衍了事,而成为一个…那个叫什么?程序员?但那是个受人尊敬的职业么?什么又是编程呢?它作为一门学科所需要的完备知识体系又在哪里呢?我非常清晰得记得自己有多嫉妒我从事硬件的同僚们,因为当被问到专业技能时,他们至少可以谈自己所知道关于真空管,放大器,和其他一些硬件的一切;而被问到这个问题时,我只能茫然无措。满心焦虑,我敲开了 van Wijngaarden 的办公室大门,问道:“是否能占用他一点时间”;而在几个小时后离开他办公室时,我整个人焕然一新。在耐心听完我的问题后,他同意当时并没有学科是关于编程的,但是他接下来解释说,自动化电脑会继续发展,而我们正处于摸索阶段,那我能不能成为先驱者之一,使编程在未来成为一个受人认可的学科呢?这成了我人生中的一个转折点,促使我尽快完成了我的物理学研究。上述故事的寓意是,我们必须谨慎地向年轻人提供意见,因为有时他们真的会听的!

荷兰婚礼仪式上有要求新人陈述职业的传统,而两年后的1957年,当我结婚时我宣告自己是一名程序员。但是阿姆斯特丹的市政当局没有接受,理由是并不存在程序员这样一种职业。不管你信不信,在我婚姻证书上“职业”这一栏里,可笑的写着“理论物理学家”!

关于程序员作为一种职业在我国的缓慢出现,就说这么多了。从那时起,我见识到了外面的世界,留下的总体印象是,尽管起步的时间不同,但是其他国家的发展模式是大同小异的。

让我尝试更多地回顾下昔日的一些细节,以期更好地理解现在的情况。现在我们对问题进行分析时,应该知道,关于编程任务很多常见误解是可以追溯到很久以前的。

第一代的电子计算机是独特的机器,并且只能在各种进行开拓性试验的实验室里被找到。从人类见识了自动化计算机那天起,实现它就是对当时现有的电子技术一个巨大挑战,不过有一件事很确定,那就是我们不能否认那些致力于开发这些设备先驱们的勇气。这些设备是如此的令人惊奇,回想起来,至少是有时候,人们只能怀疑,这些机器是否真的运行起来了。最难解决的问题就在于让机器跑起来并让其保持工作秩序。一些较老的计算机科学学会的命名中依然反映着自动计算机硬件方面的当务之急,如美国计算机协会(ACM)和英国计算机协会,这些协会的名称中明确提到了物理设备。

那么可怜的程序员又是什么样的处境呢?实情是:他根本没有被人注意。一方面,第一代计算机是如此得笨重,以至于无法搬来搬去,同时,它们又需要大规模的维护,因此很自然的,人们就在发明这些计算机的实验室里使用它们。另一方面,程序员的工作既不为人所知又缺少魅力,在打动参观者的程度上,展示机器比展示几页代码要高出几个数量级。但是最重要的是,程序员以非常谦逊的眼光看待自己的工作:程序员工作的所有重要性都继承自硬件设备。因为每一台计算机都是不同的,程序员非常清楚,他的程序只有在特定计算机上才能运行,并且由于计算机的寿命有限,导致他的工作会随着机器的老化而丧失价值。最后,还有一种情况对程序员的工作态度产生了深远的影响:一方面,除了不稳定,他工作的机器一般还存在运行缓慢、内存太小等问题,这让巧妇难为无米之炊;另一方面,程序中通常又会存在少量古怪代码来应对突发的状况。在那些日子里,有很多聪明的程序员通过一些巧妙的方法,使他的设备可以完成一些本来不可能完成的任务,以这样的方式来获得智力上的巨大满足感。

关于编程的两种观点就起源于那个时期。我现在提下这件事,等下会回到这个话题。一种观点认为真正专业的程序员应该具有解决问题的头脑,并且非常喜欢聪明的伎俩;另一种观点认为,编程就是在某个方向上优化计算效率的过程。

后一种观点源自于设备落后的年代,那时候有些人简单的期望着,一旦机器变得更为强大,编程将不会成为难题,到那时想尽办法榨干机器的性能将不再有必要,这就是编程的目的,难道不是吗?但是下一个十年里发生了巨变,更为强大的机器被造出来了,不是一个数量级的强大,而是好几个数量级的强大。但是出人意料的是,我们并没有一劳永逸地解决所有的问题,反而发现自己处在了软件危机中!这是怎么回事呢?

一个小小的原因是:现代计算机在一个或两个方面比老式的计算机更难以操作。首先,现代计算机有I/O中断,中断的发生不可预知也不可重现,这个特性与能完全掌控运行过程的旧式计算机相比,是一个巨大的变化,但是这个特性会产生难以预料的逻辑错误,很多程序员花白的头发见证了这一点。第二,现在的计算机配置了多级存储设备,关于设备管理的策略,虽然已经有大量的文献,但是对我们来说仍然相当难以捉摸。关于计算机结构的变化所增加的复杂性就讲这么多了。

但是上面所举的只是“小”原因,真正大的原因是…现代计算机性能比原来强大了好几个数量级!可以不客气的说:当世上没有计算机的时候,根本就不会有编程问题;当我们有了几台性能弱小的计算机后,编程就出现小问题了,现在我们有了性能强大的计算机,编程自然变成了巨大的问题。在这样的发展过程中,电子产业非但没有解决一个问题,而是产生了问题,即使用产品所带来的问题。换种方式说,如果现有计算机性能增长了千分之一,那么社会对于使用这些机器的期望也会以同样的比例增长,而可怜的程序员会发现自己处于到底是目的优先还是方式优先的紧张选择中。硬件性能的提升,加上其可靠性甚至更急剧的提升,使得几年前程序员做梦都不敢想的问题变得可以实现。而几年后,他不得不梦见这个问题,更糟的是,他不得不将这样的梦变成现实!那么我们发现自己仍身处于软件危机中是不是一个奇迹呢?当然不是了,正如你猜测的那样,这些甚至是可以提前预知的,只不过这种小众预测的问题在于五年后你才能知道它是正确的。

接着在六十年代中期,有一件可怕的事情发生:所谓的第三代计算机出现了。官方的文献表示,性价比一直是他们主要的设计目标之一。但是如果你将机器众多组件的占空比作为“表现”的衡量标准,你会看到一个内存被必要性存疑的内部常驻活动所占据的设计。如果你对于价格的定义是硬件的价值,你会看到一个对其编程极其困难的设计:举个例子,无论对于程序员还是系统,用于执行的指令码早期绑定所带来的问题都不能真正得到解决。并且在很大程度上,这些令人不快的可能性看起来已经成为了现实。

当这样的机器宣布发售,其功能规格为人所知后,我们之中的不少人肯定相当难受,至少我是这样。第三代计算机的发行肯定会席卷计算机界,而人们更加期待它的设计能够尽量健全。但是因为这样一个有着如此严重缺陷的设计,让我感觉到计算机科学至少退步了十年:这也让我的职业生涯经历了最为黑暗的一个星期。也许最可悲的事情是,即使有了这么多年令人沮丧的经验,依然有如此多的人天真的相信计算机必须是这样。他们用机器的销量来打消自己的疑虑,并且从这种观察中得到使用这些机器是安全的错觉,毕竟,机器的设计不可能那样糟糕。但是仔细想想,这种被销量欺骗的安全感就和吸烟必须是健康的说法相差无几,因为吸烟的人很多。

正是这方面,我很遗憾,在发表对最新公布的计算机的评论时,计算机领域的科学期刊并没有习惯像其他科学出版物那样做出复查:对机器的复查至少应该同样重要。这里我要检讨:在六十年代早期,我写过这样的评论并准备提交给CACM,尽管我没有向同事们寻求建议,但他们极力主张我这样做,可我还是不敢,因为担心这样无论对我还是对编委会来说,困难都太大。是懦弱没有让我提交这样一份报告,对此我越来越自责。我预见到的困难是当时缺乏普遍接受的标准,虽然确信自己的标准是正确的,但我还是担心我的报告会被拒绝,或者被贴上“个人口味问题”的标签。我仍然认为这样的报告是及其有用的,并且渴望着它们的出现,因为它们的出现将代表着计算机界的成熟。

之所以对硬件保持上面提到那样的关注,是因为我相信影响那些使用者的思考习惯是任何计算工具最重要的作用之一,并且我有理由相信,这种影响力比预期的更为强烈。现在让我们的注意力回到软件上。

由于软件具有显著的多样性,我必须将自己限制在几个立足点上。这使得我做出的选择显得很武断,也让我觉得很痛苦,希望大家不要因此怀疑我对其他没有提到的努力的欣赏。

刚开始的时候,EDSAC 在英国的剑桥兴起,我对其起初让子程序库在机器设计和使用方法中发挥主要作业的观点印象深刻。接近25年后的今天,计算领域彻底的变化了,但是软件的基本观念还伴随着我们,闭合子程序的观念也还是我们编程的核心概念之一。我们应该认可闭合子程序作为最伟大的软件发明之一,它经历了三代计算机的发展并且仍将继续存活几代,因为它迎合了我们基本抽象模式的接口思想。遗憾的是,在第三代计算机的设计中,它的重要性被低估了,而这个设计中,大量明确命名为寄存器的算术计算单位揭示了子程序机制的广泛使用。尽管如此,也没有让子程序的概念就此消失,我们只能祈祷这种变化不会在下一代的计算机中被遗传。

我要讲的第二个软件业的主要发展是FORTRAN的诞生。在当时这是一个伟大而冒失的计划,负责这个计划的人们值得我们大声地赞美。因为大约10年后才日渐明显的外延错误用法而去责备他们是显然不公平的:能够成功预测十年后的团队相当罕见。当我们回顾过去时,必须把FORTRAN看做一项成功但是对其产生概念基本没啥帮助的编码技术,在需求如此迫切的现在,这项技术已经过时了。我们应该尽快忘记FORTRAN,对于思想的马车而言,它不再胜任:它浪费了我们的脑力,使用它过于冒险,因此也太过奢侈。FORTRAN的悲惨命运已经因为它广泛的接受度,影响了数以千计的程序员为我们过去的错误买单。我每天都祈祷,会有更多程序员小伙伴找到从通用性的祸害中解脱出来的方法。

第三个不能忽略的就是LISP,一个完全与众不同的迷人设计。基于LISP的几个基本原则使得LISP展示出了非凡的健壮性。除此以外,LISP成为了相当多的复杂计算机载体。LISP曾经被调侃为滥用电脑的最智能方法。我想这种描述是一种很高的赞美,因为它传递了解放的气息——它已经帮助我们之中最有天赋的人思考了之前不可能思考的东西。

第四个要提到的就是 ALGOL 60。到目前为止,FORTRAN程序员仍然倾向于从他们的开发中了解他们的编程语言——八进制的兴起和十六进制的衰落;而LISP的定义依然是一种令人好奇的混合体,其中包括这门语言意味着什么和这门机制是怎么工作的,《算法语言》那篇著名的报告中说道,ALGOL 60是真正努力去承载抽象概念至关重要的一步,再加上通过独立于语言之外的方法定义一门编程语言的成果。有人会说,ALGOL 60的作者们已经如此的成功,以至于他们自己引出这门语言是否可以实现产生的严肃疑问!那篇报告冠冕堂皇地展示了BNF范式(即现在人尽皆知的Backs-Naur-Form)的作用,和英语谨慎措辞的力量,至少是像Peter Naur一样聪明的人使用时。我觉得可以负责任地说,基本没有像它这么短而在计算机界有相同影响力的文档存在。人们习惯于使用用“ALGOL”或者是“类ALGOL”这样未经保护的标签,向大量基本不相关的年轻语言分享ALGOL的荣耀,这本身某种程度上就是对ALGOL惊人生命力的极大致敬。BNF作为一种严格定义的语言,它的长处导致了我个人认为是这门语言的缺点——过分精心制作并且语法定义不规则,很难在几页纸中完全说明。如果有个BNF般强大的设备,《算术语言》中那篇关于ALGOL60的报告可以变得更短很多。除此以外,我对ALGOL60的参数机制表示怀疑,它赋予程序员如此多的组合自由,以至于要求程序员更自律。除了开发成本外,它使用起来似乎也很危险。

最后,尽管令人不太愉快,但我必须提及——PL/1,一门描述文档非常庞大而且复杂的编程语言。使用PL/1就像开着一架有着7000个按钮,使用开关和扳手操作的飞机。我完全不能理解怎么靠智力来把握这种完全怪异的编程语言(我们的基本工具),使之保持稳定的发展,提醒你,这已经脱离我们智力的掌控了。还有如果非要描述PL/1对其用户的影响,我脑海中最为形象的隐喻就是毒品。我记得这个出自一个自称是PL/1的忠实用户在一个高级语言的专题讨论会上做的一个演讲。但是,在这个为期一小时充满对PL/1赞美的演讲中,他希望大家添加50种新特性,却没有想过他问题的源头是这门语言已经包含了太多的特性。演讲者展示了所有令人沮丧大的症状,他陷入内心停滞,但是渴求更多。当FORTRAN被称为婴儿期的凌乱时,完整的PL/1却是危险的肿瘤,最后成长为了一种致命病毒。

关于过去的事,就讲这么多吧。但是如果我们不能从中汲取教训的话,制造错误是没有任何意义的。事实上,我觉得我们学习了如此多的错误,以至于几年后的编程可以和之前截然不同。让我简述下你们可能的未来。乍看一下,这种编程的想象也许会让你不久的将来变得非常的不切实际。所以也允许加上也许会让人得出“这个人的想象相对实际”的结论的考虑吧。

这种想象就是,在七十年代结束之前,我们有可能设计和实现那种尽量利用编程能力,开支是许多年前百分之几的系统。此外,这些系统可以几乎摆脱bug。这两项提升相辅相成。在后一个方面,软件看上去和许多其他产品不同,很多产品质量和价格是成正比的。那些想要真正有效的软件的人将会发现他们必须从找到避免大多数bug的方法开始,最后,编程过程将变得便宜。如果你想要一些有能力的程序员,你将发现他们不会浪费时间在调试上,他们不应该以调试bug作为开始。换句话说,两种目标指向同样的变化。

在如此短的一段时间内发生如此激烈的变革,这会是一场革命,对所有人来说,基于他们对未来的期望,对过去(诉诸不成文的社会,文化惰性的法律)做出的和平推断,这种巨大变革会发生的机会几乎微不足道。但我们都知道有时候革命真的发生了。 那么这次变革我们的机遇是什么呢?

似乎有三个主要条件需要满足。一般来说,世界必须承认需要变化,第二经济需求十分强烈,还有第三,变革在技术上是可行的。让我讨论按顺序讨论一下上述三个条件。

谈到对软件更为可靠的需求,我表示完全同意。在几年前,就不一样了——讨论软件危机是对上帝的亵渎。转折点是 1968年十月在Garmisch举行的软件工程的学术会议,一次萌生了知觉承认软件危机的回忆。到目前为止,普遍认为任何大型系统的设计都是一项十分困难的工作,无论何时,人们遇到负责这样工作的人,都会发现他们非常关心可靠性问题。简而言之,我们第一个条件似乎满足了。

现在说到经济需求。现在人们总是遇到说六十年代编程是一个薪酬过高的职业的观点,并且说未来几年内,程序员薪酬希望可以下降。通常,这种观点的表述与经济衰退有关,但这是一种不同,健康的事物的征兆——也许过去几十年里程序员没有很好完成他们应该完成的工作。社会对程序员的表现和他们的产品感到不满意。但还存在另外一种导致重担的因素。现在的情况是,对于特定的系统,为软件升级的价格和升级硬件的价格在同一个数量级,社会或多或少接受了这一点。但硬件制造商告诉我们说在接下来数十年,硬件价格将会以十倍速度下降。如果软件发展还是一如既往的笨拙,程序一如既往的昂贵,局势将失去平衡。你不能指望社会接受这一点,因此我们必须学会有效的编程。 换句话说,只要机器还是预算中的大项,那么笨拙的编程技术还能侥幸逃脱,但是保护伞会快的折起来。总之,我们的第二个条件似乎也能满足。

现在轮到第三个条件,技术上可行吗?我觉得可行,我会用六个论据支撑我的观点。

一份程序结构的研究表明程序,即便是同一个任务的替补程序和有着相同的数学内容,在程序管理方面都有着巨大的差异。研究发现许多规律,违反任何一条规律要么局部或者整体的破坏程序的人为管理。这些规律不外乎两种。第一种容易在物理上加以利用,即选用合适的编程语言。Goto语句的和多输出参数的程序的剔除是很好的佐证。对于第二种,我至少看不到物理上使之暴露的方法(可能是我能力不足),好像需要一些我没有证明存在的自动化的定理证明程序。因此,第二种规律时不时作为程序员训练的要素之一出现。有些规律我清楚的知道以至于可以教给他人和不需要判断一段给定的程序是否违反了。 我们给出必要的例子——任何循环需要给出终止条件或者声明不变量之间的关系保证不变量不会被程序的循环语句执行而破坏。

我现在提议把我们限制在智能可控程序的设计和实现的范围内。如果有些人害怕这种限制过于严格,我们没有办法存活,我保证智能可控程序的种类足够的丰富,可以囊括现实很多能够解决算术问题的程序。我们不能忘记我们的职业不是为了编程,而是设计能够表现预设行为的计算类别。把我们限制在智能可控程序的建议是我所谓的六个论据的前两个的基础。

论据一,程序员只要考虑智能可控问题,选择更为容易考虑。

论据二,我们一决定把我们限制在智能可控程序的子集内,我们就一劳永逸的实现了考虑解决方案的空间的减少。

论据三是建立在程序正确性问题的建设性方法上的。今天,一项普通的技术就是写一个程序,然后去测试。 尽管,程序测试是一种非常有效的方法去暴露bugs,但对证明不存在bugs几乎是完全没用的。显著提高程序可信度唯一有效的方法是给出一个令人信服的关于正确性的证据。但是我们不应该首先写出程序,然后去证明它的正确性,因为要求证明只会增加苦逼程序员的负担。相反,程序员应该让正确性证明和程序相互验证,发展。论据三本质上是从以下的观察得来的。如果一个人问自己一个令人信服的证据应该具备什么,他了解后,写了一个很好的满足了证明要求的程序,然后这些关于正确性的担心变成一种有效的启发式的指导。当我们把自己限制在智能可控程序时,按照定义,只有这种方法是可行,但这种方法也提供许多有效的方法,让我们从中挑选一个满意的。

设计一个程序需要多少人力根据程序的长度判断,论据四与此相关。曾经有人指出存在一种自然法则告诉我们耗费人力是程序长度的平方。但是谢天谢地,还没有人能够证明这种法则。这是因为它需要是假的。我们都知道能够解决很多问题的推理论证工具称之为抽象,抽象能力的有效开发可以被认为一个有能力的程序员至关重要的敏捷之一。关于这一点,值得指出抽象的意图并不是为了模糊本身,而是去创造新的准确语义水平。当然,我曾经尝试去找到能够阻止我们抽象机制起作用的基础原因。但不管我多么努力,我都找不到任何一个原因。最后,我倾向于假设(直到现在未通过经验证明)我恰当的抽象化能力,也就是人力需要想象或者理解一个程序的耗费不会根据程序长度按照超过正比例的速度增长。但是这些调查的一个衍生品更有实际意义,实际上,它是我论据四的基础。衍生品是许多在整个编程过程中扮演重要角色的抽象模式的识别。你了解这些抽象模式足够多,你就可以就每个模式作报告。这些抽象模式的熟悉和了解让我幡然醒悟,例如从BNF到方向句法编译,可以用几分钟代替几年的花费。 因此,我把我们最近对重要抽象模式的理解作为论据四。

现在说到论据五,它和我们建立在我们思考习惯上的工具的影响有关。我观察到一种十有八九来源于文艺复兴的文化传统,为了忽略这种影响,需要把人心视为肉体最高自主的主人。 但我一开始分析我和同类的思考习惯时,我得到一个完全不同的结论,不管我喜欢与否,即我们使用的工具和我们用来表达或者记录我们想法的语言或记号,是决定我们能够想到什么的主要因素。对编程影响程序员的思考习惯的分析和脑力是我们几乎不曾使用的资源的认识,它们给出了从多方面给出不同编程语言优缺点的尺度。一个有能力的程序员能够意识到他自己脑容量的严格尺寸,因此,他谦逊的完成编程任务,牵涉到其他事情时,他避免小聪明像躲避瘟疫一样。说到一种著名的会话式程序设计技术,我从不同渠道被告知说如果一个计算社区一旦装备终端,一种特殊的现象就会发生,这种现象甚至有了一个被广泛接受的名称——打趣的人。它有一到两种不同的形式,一个程序员在别人桌面放了一个调侃的程序,他要么骄傲的展示它的用途并问别人你能用更少的字符写完吗(好像有任何概念的相关性一样),要么就问说猜猜这是用来干嘛的。通过这个观察,我们必须总结说这门语言是一种宣扬奇技淫巧的工具,同时这会是它的一些诉求的解释,例如说那些喜欢展示他们有多聪明的人,对不起,但是我必须把它视作关于编程语言最令人恶心的地方。我们必须从过去学到的另外一课是所谓功能强大,丰富的编程语言的发展是一种错误,就像这些奇怪庞然大物,某人特有的气质的聚集,都是心灵上,肉体上不可控的。我从系统的谦逊的编程语言上看到很美好的未来。 当我说谦逊时,我的意思是,例如,不仅是ALGOL60的for分支语句,还有FORTRAN的DO循环语句发现因为变得过于怪异而被淘汰。我曾经和一些有经验的志愿者做过一点编程实验,但有些不期望发生和预料不到的事情发生了。我的志愿者没有一个找到明显而漂亮的解决方法。通过更为细致的观察,我们找到相同的源头:他们对与重复的概念与用于加速的联合控制变量的联系过于紧密,以至于他们被蒙蔽了完全看不到明显的方法。他们的方法低效,晦涩难懂,并且耗费了大量时间。这是一段对我来说具有启示作用,和令人震惊的经历。最后,从某个方面上来讲,如果一个人希望明天的编程语言和现在的大为不同,很大程度的优于现在的,他们应该邀请我们在我们写下的所有需要用来应付概念上我们如今设计的抽象结构中思考。关于我们未来工具的优越性,也就是我们第五个论据的基础,就讲到这里。

作为旁观者,我希望提醒一下那些以现有工具不足而觉得编程任务困难的人。编程依旧会十分困难因为一旦我们从详尽的笨重中解放出来,我们将会发现自己得以面对解决远在我们编程能力以外的问题。

你可以不同意我的第六个论据,因为很难收据支持的实验证据,但不能阻止我相信它的正确性。直到现在,我还没有提到分层这个词,但我认为把它作为一个所有收录着一个精细分解解决方案的系统的核心概念是合理的。我甚至可以走更远一步写一篇关于信仰的文章,即我们能够以令人满意的方式解决的唯一问题是那些最后接受精细分解解决方案的问题。乍看之下,这种人类局限的观点是一种令人抑郁的关于我们处境的观点,但我恰恰相反,不这么觉得。学会和我们的局限共存的方法就是了解我们的局限。等到我们足够谦逊去尝试分解方案的死后,因为其他形式的努力已经超过我们智力的掌控,我们将要尽最大的努力去避免所有这些内部损害我们以有用的方式分解系统的能力的交流。我不得不希望这不停的引领我们发现一个容易解决的问题最终也是可以分解的。任何一个看到了大多数所谓的“代码生成”的编译状态的麻烦都可追溯到指令码的有趣特性的人,都会知道一个我在脑海中想到的事情简单示例。适用性更好的分解良好的解决方案是我第六个也是最后一个论据用于支持技术上可行的可能会在这十年发生的革命。

原则上,我把这些论据留给你,让你决定我的考察占多少分量,因为我很清楚我没有办法强迫任何人跟随我的信仰。每次重要的变革都会招致剧烈反对,每个人都可以问自己希望保守力量抵消多少发展。我不指望主要的大财团,甚至计算机领域的集团,我宁愿希望教育机构提供培训,希望那些保守团体的计算机用户认为他们古老的程序很重要以至于不值得重写或者优化。关于这一点,很多大学校园的中心电脑设备的选择视一些现成但昂贵且不理会千千万万愿意自己编程的小用户的问题的程序的需求而定,学校将为他们的选择埋单,这是相当可悲的。这种事情太常发生,例如,高能物理似乎曾经用他们剩下的实验设备勒索科学社区。当然,最容易的解决方法,是对技术可行性的干脆的拒绝,但我担心你需要足够支撑这种方法的论据。唉,现在普遍程序员的脑力水平可以阻止变革发生是得不到任何保证的,因为其他人的编程变得更为有效,程序员很容易在社会的版图中被边缘化。

还有很多政治方面的障碍。即便我们知道怎么去培养未来的专业的程序员,我们不能确定社会允许我们这样做。传授方法论(而不是散布知识)的第一影响是增强了现有的能者的能力,因此拉大了智商上的差异。在一个教育系统是用来建立平均文化的社会,在一个精英不允许出现在顶层的社会,培养有能力的程序员在政治上是不对味的。

让我总结一下。计算机已经伴随我们走过一个世纪的四分之一了。它们以工具的身份对我们社会产生了深远的影响,但是和在人类历史上史无前例的以智力挑战的形式产生深远的影响相比,它们只能以波浪的形式影响我们文化的表面。似乎拥有决定在某种程度上将物体视作一个不可分割整体的权利的分层系统,被视作一个级别更低,细节更多的混合物,最后,当我们将注意力从一级转向更低一级时,时空的天然粒面会以合适的数量级减少。 我们知道墙以砖为单位,砖以结晶体为单位,结晶体以分子为单位,等等。在分层系统中,许多级别可以有意义的区分出来,这和最大与最小粒度的比率的对数成正比,因此,除非比率很大,我们不能指望能够分很多层。在计算机编程中,我们基本的编程语句有低于一微秒的时间粒度,但我们的程序却可能花费几小时的计算时间。我不知道任何一种超过10的10次方的比率的技术,以它超凡速度为优点的计算机,似乎是第一个给我们提供需要分层粒度大和实现它的环境的。这样的挑战,即面对编程任务,是独一无二的,因此这样小说一般的经历可以教会我们很多关于自己的知识。它加深了我们对设计和创造程序的理解,它帮助我们更好的完成想法条理化的任务。如果它没有这样做,对我来说,我们根本不值得拥有计算机。

它已经给我们上了几课,在这次演讲中我要强调以下几点。我们应该更好的编程,证明我们用满是对任务带来的巨大困难的感激的方法完成任务,证明我们坚持谦逊和优雅的编程语言,证明我们尊重人类内心固有的限制和以谦卑的程序员的身份完成任务。

参考:原文地址

XCode空白工程,手工添加Storyboard步骤

XCode新建空白工程后,手工增加Storyboard并添加ViewController后,只显示空白,不显示ViewController:
调试时会提示:

Application windows are expected to have a root view controller at the end of application launch

这时,需要手工修改一处代码和一处配置:
1、AppDelegate源码文件

//修改前
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    // Override point for customization after application launch.
    self.window.backgroundColor = [UIColor whiteColor];
    [self.window makeKeyAndVisible];

    return YES;
}

//修改后
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    return YES;
}

2、指定启动的Storyboard
在Project-》Targets-》Summary中修改Main Storyboard属性为新添加的Storyboard名称(不带后缀)。
或者
修改XXX-Info.plist文件,添加属性

<key>UIMainStoryboardFile</key>
<string>Storyboard名称(不带后缀)</string>

然后就OK啦。

Linux kernel management style


Linux kernel management style
by Linus Torvalds

This is a short document describing the preferred (or made up, depending on who you ask) management style for the linux kernel. It’s meant to mirror the CodingStyle document to some degree, and mainly written to avoid answering (*) the same (or similar) questions over and over again.

Management style is very personal and much harder to quantify than simple coding style rules, so this document may or may not have anything to do with reality. It started as a lark, but that doesn’t mean that it might not actually be true. You’ll have to decide for yourself.
Btw, when talking about “kernel manager”, it’s all about the technical lead persons, not the people who do traditional management inside companies. If you sign purchase orders or you have any clue about the budget of your group, you’re almost certainly not a kernel manager. These suggestions may or may not apply to you.

First off, I’d suggest buying “Seven Habits of Highly Successful People”, and NOT read it. Burn it, it’s a great symbolic gesture.

(*) This document does so not so much by answering the question, but by making it painfully obvious to the questioner that we don’t have a clue to what the answer is.

Anyway, here goes:

Chapter 1: Decisions

Everybody thinks managers make decisions, and that decision-making is important. The bigger and more painful the decision, the bigger the manager must be to make it. That’s very deep and obvious, but it’s not actually true.

The name of the game is to _avoid_ having to make a decision. In particular, if somebody tells you “choose (a) or (b), we really need you to decide on this”, you’re in trouble as a manager. The people you manage had better know the details better than you, so if they come to you for a technical decision, you’re screwed. You’re clearly not competent to make that decision for them.
(Corollary:if the people you manage don’t know the details better than you, you’re also screwed, although for a totally different reason. Namely that you are in the wrong job, and that _they_ should be managing your brilliance instead).

So the name of the game is to _avoid_ decisions, at least the big and painful ones. Making small and non-consequential decisions is fine, and makes you look like you know what you’re doing, so what a kernel manager needs to do is to turn the big and painful ones into small things where nobody really cares.

It helps to realize that the key difference between a big decision and a small one is whether you can fix your decision afterwards. Any decision can be made small by just always making sure that if you were wrong (and you _will_ be wrong), you can always undo the damage later by backtracking. Suddenly, you get to be doubly managerial for making _two_ inconsequential decisions – the wrong one _and_ the right one.

And people will even see that as true leadership (*cough* bullshit *cough*).
Thus the key to avoiding big decisions becomes to just avoiding to do things that can’t be undone. Don’t get ushered into a corner from which you cannot escape. A cornered rat may be dangerous – a cornered manager is just pitiful.

It turns out that since nobody would be stupid enough to ever really let a kernel manager have huge fiscal responsibility _anyway_, it’s usually fairly easy to backtrack. Since you’re not going to be able to waste huge amounts of money that you might not be able to repay, the only thing you can backtrack on is a technical decision, and there back-tracking is very easy: just tell everybody that you were an incompetent nincompoop, say you’re sorry, and undo all the worthless work you had people work on for the last year. Suddenly the decision you made a year ago wasn’t a big decision after all, since it could be easily undone.

It turns out that some people have trouble with this approach, for two reasons:

– admitting you were an idiot is harder than it looks. We all like to maintain appearances, and coming out in public to say that you were wrong is sometimes very hard indeed.

– having somebody tell you that what you worked on for the last year wasn’t worthwhile after all can be hard on the poor lowly engineers too, and while the actual _work_ was easy enough to undo by just deleting it, you may have irrevocably lost the trust of that engineer. And remember: “irrevocable” was what we tried to avoid in the first place, and your decision ended up being a big one after all.

Happily, both of these reasons can be mitigated effectively by just admitting up-front that you don’t have a friggin’ clue, and telling people ahead of the fact that your decision is purely preliminary, and might be the wrong thing. You should always reserve the right to change your mind, and make people very _aware_ of that. And it’s much easier to admit that you are stupid when you haven’t _yet_ done the really stupid thing.

Then, when it really does turn out to be stupid, people just roll their eyes and say “Oops, he did it again”.

This preemptive admission of incompetence might also make the people who actually do the work also think twice about whether it’s worth doing or not. After all, if _they_ aren’t certain whether it’s a good idea, you sure as hell shouldn’t encourage them by promising them that what they work on will be included. Make them at least think twice before they embark on a big endeavor.
Remember: they’d better know more about the details than you do, and they usually already think they have the answer to everything. The best thing you can do as a manager is not to instill confidence, but rather a healthy dose of critical thinking on what they do.

Btw, another way to avoid a decision is to plaintively just whine “can’t we just do both?” and look pitiful. Trust me, it works. If it’s not clear which approach is better, they’ll eventually figure it out. The answer may end up being that both teams get so frustrated by the situation that they just give up.

That may sound like a failure, but it’s usually a sign that there was something wrong with both projects, and the reason the people involved couldn’t decide was that they were both wrong. You end up coming up smelling like roses, and you avoided yet another decision that you could have screwed up on.

Chapter 2: People

Most people are idiots, and being a manager means you’ll have to deal with it, and perhaps more importantly, that _they_ have to deal with _you_.

It turns out that while it’s easy to undo technical mistakes, it’s not as easy to undo personality disorders. You just have to live with theirs – and yours.

However, in order to prepare yourself as a kernel manager, it’s best to remember not to burn any bridges, bomb any innocent villagers, or alienate too many kernel developers. It turns out that alienating people is fairly easy, and un-alienating them is hard. Thus “alienating” immediately falls under the heading of “not reversible”, and becomes a no-no according to Chapter 1.

There’s just a few simple rules here:

(1) don’t call people d*ckheads (at least not in public)
(2) learn how to apologize when you forgot rule (1)

The problem with #1 is that it’s very easy to do, since you can say “you’re a d*ckhead” in millions of different ways (*), sometimes without even realizing it, and almost always with a white-hot conviction that you are right.

And the more convinced you are that you are right (and let’s face it, you can call just about _anybody_ a d*ckhead, and you often _will_ be right), the harder it ends up being to apologize afterwards.

To solve this problem, you really only have two options:
– get really good at apologies
– spread the “love” out so evenly that nobody really ends up feeling like they get unfairly targeted. Make it inventive enough, and they might even be amused.

The option of being unfailingly polite really doesn’t exist. Nobody will trust somebody who is so clearly hiding his true character.

(*) Paul Simon sang “Fifty Ways to Lose Your Lover”, because quite frankly, “A Million Ways to Tell a Developer He Is a D*ckhead” doesn’t scan nearly as well. But I’m sure he thought about it.

Chapter 3: People II – the Good Kind

While it turns out that most people are idiots, the corollary to that is sadly that you are one too, and that while we can all bask in the secure knowledge that we’re better than the average person (let’s face it, nobody ever believes that they’re average or below-average), we should also admit that we’re not the sharpest knife around, and there will be other people that are less of an idiot that you are.

Some people react badly to smart people. Others take advantage of them.
Make sure that you, as a kernel maintainer, are in the second group. Suck up to them, because they are the people who will make your job easier. In particular, they’ll be able to make your decisions for you, which is what the game is all about.

So when you find somebody smarter than you are, just coast along. Your management responsibilities largely become ones of saying “Sounds like a good idea – go wild”, or “That sounds good, but what about xxx?”. The second version in particular is a great way to either learn something new about “xxx” or seem _extra_ managerial by pointing out something the smarter person hadn’t thought about. In either case, you win.

One thing to look out for is to realize that greatness in one area does not necessarily translate to other areas. So you might prod people in specific directions, but let’s face it, they might be good at what they do, and suck at everything else. The good news is that people tend to naturally gravitate back to what they are good at, so it’s not like you are doing something irreversible when you _do_ prod them in some direction, just don’t push too hard.

Chapter 4: Placing blame

Things will go wrong, and people want somebody to blame. Tag, you’re it.
It’s not actually that hard to accept the blame, especially if people kind of realize that it wasn’t _all_ your fault. Which brings us to the best way of taking the blame: do it for another guy. You’ll feel good for taking the fall, he’ll feel good about not getting blamed, and the guy who lost his whole 36GB porn-collection because of your incompetence will grudgingly admit that you at least didn’t try to weasel out of it.

Then make the developer who really screwed up (if you can find him) know _in_private_ that he screwed up. Not just so he can avoid it in the future, but so that he knows he owes you one. And, perhaps even more importantly, he’s also likely the person who can fix it. Because, let’s face it, it sure ain’t you.

Taking the blame is also why you get to be manager in the first place. It’s part of what makes people trust you, and allow you the potential glory, because you’re the one who gets to say “I screwed up”. And if you’ve followed the previous rules, you’ll be pretty good at saying that by now.

Chapter 5: Things to avoid

There’s one thing people hate even more than being called “d*ckhead”, and that is being called a “d*ckhead” in a sanctimonious voice. The first you can apologize for, the second one you won’t really get the chance. They likely will no longer be listening even if you otherwise do a good job.

We all think we’re better than anybody else, which means that when somebody else puts on airs, it _really_ rubs us the wrong way. You may be morally and intellectually superior to everybody around you, but don’t try to make it too obvious unless you really _intend_ to irritate somebody (*).
Similarly, don’t be too polite or subtle about things. Politeness easily ends up going overboard and hiding the problem, and as they say, “On the internet, nobody can hear you being subtle”. Use a big blunt object to hammer the point in, because you can’t really depend on people getting your point otherwise.

Some humor can help pad both the bluntness and the moralizing. Going overboard to the point of being ridiculous can drive a point home without making it painful to the recipient, who just thinks you’re being silly. It can thus help get through the personal mental block we all have about criticism.

(*) Hint: internet newsgroups that are not directly related to your work are great ways to take out your frustrations at other people. Write insulting posts with a sneer just to get into a good flame every once in a while, and you’ll feel cleansed. Just don’t crap too close to home.

Chapter 6: Why me?

Since your main responsibility seems to be to take the blame for other peoples mistakes, and make it painfully obvious to everybody else that you’re incompetent, the obvious question becomes one of why do it in the first place?

First off, while you may or may not get screaming teenage girls (or boys, let’s not be judgmental or sexist here) knocking on your dressing room door, you _will_ get an immense feeling of personal accomplishment for being “in charge”. Never mind the fact that you’re really leading by trying to keep up with everybody else and running after them as fast as you can. Everybody will still think you’re the person in charge.

It’s a great job if you can hack it.

参考:原文地址

Linux内核管理风范


Linux内核的管理风格
Linus Torvalds

这个简单文档描述Linux内核偏爱的(或编造的,取决于你问谁)管理模式。它在一定程度上是编码风格文档的影子,主要写来避免一遍又一遍回答同一类问题*。

管理风格是很个人化的,比起简单的编码风格条例更难量化,所以这个文档跟现实可能沾边也可能不沾边。它开始于游戏,但是不见得就不作数。你只有自个儿决定。
顺便说一下,我们说到”内核管理者”的时候,完全是说技术带头人,不是公司里那些作传统管理工作的人。如果你是在订单上签名的人或者对你们组的预算知道一丁半点,你几乎一定不是个”内核管理者”。这些建议对你可能适用也可能不适用。
首先,我建议你买一本《高度成功人士的七个习惯》,不-要读它,烧了。表一下决心。
(*) 这个文档不见得”回答”多少问题,更大程度上是展示我们的无知,让提问者死了这条心。

不管怎样,开讲了:

第一章:决定

每个人都觉得管理者是作决定的,作决定是很重要的。决定越大、越艰难,管理者就越伟大。这一点很深刻、很明显,但不见得正确。

事情的要义是避免-作决定的必要性。特别是,当有人告诉你”是甲还是乙,我们需要你来作决定”,你作管理的麻烦就来了。你手下的人一般比你更懂具体问题,所以要是他们找你作一个技术性的决定,你死定了。要替他们作决定,你显然水平不够。

(推论:如果你手下的人不比你更懂具体问题,你还是死定了,尽管出于完全不同的原因。说白了就是你站错了岗位,应该他们-来管理你的才华才对。)

所以要义是避免-决定,至少避免大的和艰难的决定。作小的、不重要的决定还好了,而且让你挺挺板板、面上有光。所以一个内核管理者需要的是把大的艰难的决定变成没人在乎的小事情。
帮你点拨一下,大决定和小决定的区别在于你能否事后修补得了。如果你犯错了的时候(而且你会-犯错),你能返回来弥补损失,那么你可以把任何的决定变成”小决定”。一下子,你的表现机会多了一倍:你要作两-个不重要的决定,错误决定”加上”正确决定。
而且大家甚至会认为这是领导才能(咳咳,狗屁,咳咳)。因此避免作重大决定的要点成了仅仅避免做不可逆反的事情。不要被牵引到一个无路可逃的角落里。困在角落里的耗子或许是危险的,困在角落里的管理者不过是个可怜虫。
事实上,不管怎样-,没有人会愚蠢到让一个内核管理者承担太大的财政责任,所以纠正错误一般不是多难。既然你没有机会浪费掉你倾家荡产也还不清的巨额经费,你要纠正的不过是一个技术性的决定。那就好办了:告诉每个人你是个不称职的白痴,说对不起,把你让大家上一年作的无用功都扔掉。一下子,你一年前作的决定也不见得是什么重大决定,既然能简单的撤销掉。
然而事实上,有些人搞不来这个办法。有两个原因:
(一)承认自己是傻瓜做起来蛮难的。我们都喜欢保持形象,公开认错有时候是很困难。
(二)对下级的工程师来说,被人告知自己上一年的工作落得一文不值,也是很恼火的。实际的工作结果-可以删除了事,但你可能就永久性的失去了这个工程师的信任。记住,”永久性”是我们要避免的第一件事,这样你的决定最终还是一个重大决定。
所幸的是,你对两个原因都可以棋高一着,未雨绸缪,防患未然。办法是:事先就承认你狗屁不懂,告诉大家你的决定不过是摸着石头过河,说不定就掉河里了。
你应该永远保留改变决定的权利,而且要让大家明白-这一点。而且在你做了真正的傻事之前承认你是个傻瓜要容易的多。
这样,当事情真的到了傻冒的地步,人们不过是翻下眼皮,说:”唉……真是灵验啊……”
这种事先承认不足的做法可能还会让下面真正做事的人三思而后行,想一下值不值。说回来,如果他们-都不确定是个好主意,你铁定了不应该给他们开绿灯,煽风点火。至少要让他们在开始大动作之前好好想一下。

记住:他们在细节上应该知道的比你多,而且他们一般觉得一切已在掌握之中。作为管理者,你能做的最好的事情不是给他们填充信心,而是给他们适量的批判理性。
顺便说一下,另外一个避免决定的办法是装可怜,简单地问”我们为什么不能两个都做呢?” 相信我,这个有用。如果不清楚哪条路子更好一些,他们最终会整明白的。答案或许是两帮人都挫败灰心,双双放弃。
这听起来像是个失败,但它一般是两个项目都有问题的迹象,大家无法决定的原因是双方都错了。结果是你成了智慧的舵手,而且你又避免了一个本来会死得很难看的决定。

第二章:人

大多数人都是傻瓜,当管理者就意味着你不得不和这一点打交道。或许更重要的是,他们-不得不和你-打交道。

事实证明,消除技术性问题还是容易的,消除人脑筋里的问题就没那么容易了。你就不得不忍受这些问题,他们的还有你自个儿的问题。

然而,为了做好内核管理者,最好记住不要自绝后路,伤及无辜,或树敌过众。现实是,疏远人们是蛮容易的,把他们拉拢回来就难了。因此”疏远”直接归类到”不可逆反”的事情里,就是第一章里说的大忌。

这里只有两条简单的规则:
(一)不要骂人猪头(至少不要在公开场合下)
(二)要是你忘了第一条,学会怎样道歉
第一条的问题是太容易违反,因为你有一万种骂人猪头的办法*,有时不自觉就骂了,而且几乎总是义愤填膺、义正严词。
而且你越是骂的热血沸腾(让我们来面对事实,你可以骂几乎任何-人猪头,你往往不-会骂错),事后你越难道歉。
要解决这个问题,你其实只有两条路子:
(一)成为道歉专家
(二)”遍洒博爱,处处留情”,这样没有人会觉得受到了特殊待遇。骂出新意,骂出水准,他们没准会找到艺术的享受。
第三条路子,始终如一的作谦谦君子,是行不通的。没有人会信任城府太深的人。
*保罗西蒙的歌唱道”失恋五十种”,是因为老实说,”骂一个程序员猪头一万种”没有那么押韵。不过我相信他肯定考虑过这个。

第三章:能人

尽管现实是大多数人都是傻瓜,不幸的推论包括你也是傻瓜之一,尽管我们都心安理得的自认比傻瓜高明(让我们来面对事实,没有人自认傻瓜或不如傻瓜),我们还是应该承认我们不是独步江湖,总会有一些人不像我们一样的傻瓜。一些人嫉贤妒能,另一些人从善如流。

确定你,作为一个内核管理者,属于第二种。贴紧了高手能人,因为他们会使你的工作变容易。特别是,他们将能够替你作决定,这正是事情的要义。
所以你要是发现了比你聪明的人,顺水推舟好了。你的管理职责很大程度上就成了说一下”听起来是个好主意,放手去干吧”,或者”这个不错,那个XXX怎么样呢?”。第二个版本尤其有效:你要么学到一些关于”XXX”的新东西,要么指出了聪明人都没想到的东西,表现得胸有-韬略。随便那种情况,你都是赢家。
另外一件要小心的事情是,一个人在一方面厉害不见得在其他方面也厉害。你或许煽动谁做什么,但是让我们来面对事实,他或许精通自己的一亩三分地却其他什么都做不来。好的消息是,人们自然而然的倾向于选择自己擅长的事情来做。所以你真的-煽动一下,一般不见得会造成什么不可逆反的后果,只是不要用铁扇公主的芭蕉扇来煽。

第四章:担当

事情总会出错的,大家会找人来责怪。哈,就是你了。

担当责任其实不是那么难的,尤其是当大家心里也有数,不全-是你的错的时候。这带来了担当责任的最好的方式:代人受过。你会因为挑起了担子而心安,那个真正搞砸了的家伙不会成为众矢之的而颓废,至于那个因为你的失职而丢失了半个硬盘的A片的家伙,也会嘟嘟囔囔的承认你至少没有猥猥琐琐的推卸责任。
然后,私下-告诉那个搞砸了的家伙是他搞砸了(如果你能发现他的话)。这样不仅让他以后避免重犯,而且让他知道他欠你一个人情。而且,或许更重要的是,他可能就是那个能修补事故的人。因为,让我们来面对事实,你肯定不行。
担当责任也是最初你来作管理者的原因。这是领导者的本分。大家能信任你,给你荣誉,是因为你在必要的时候能说”是我不好”。而且如果你已经遵循了前面的规则,你现在说这个应该很在行了。

第五章:禁区

比骂人”猪头”更招人恨的是用挖苦的语调骂人”猪头”。你可以为前一个道歉,后一个你都不会有道歉的机会。即使你其他方面都做的很好,他们可能也不会再听你的了。

我们都自我感觉良好,就是说当别人指手划脚的时候,真的-是可忍孰不可忍。你可能在才智品行上超出你周围所有的人,但是你要不是真的想-招惹谁的话*,不要”表现”得太明显。类似的,处理问题不要太客气或微妙。客气往往会隔靴搔痒,不得要害。就像人家说的,”在互联网上,没有人听得到你微妙”。大张旗鼓、锣鼓喧天的把你的意见摆出来,不然你没法指望大家领悟到。
一点幽默可以帮助润滑你的蛮横和说教。过分夸张到荒诞的地步,既能充分表达你的观点,又不至于让对方难堪,因为他只会认为你在发疯。这样就可以绕过我们每个人都有的、抵制批评的心理盲区。
*支一招:和你的工作不直接相关的网络论坛(译者按:Linus是说新闻组。没看到西方国家的论坛事业有中国这么发达。)是发泄的好地方。隔三岔五的,狞笑着写点侮辱人的帖子来点燃战火,会让你再次焕发青春。只是不要把战火烧到自己的老巢。

第六章:为什么是我?

既然你的主要职责好像就是代人受过,赤裸裸的展示自己如何不称职,显然的问题是:那你为什么还要做呢?
首要的是,可能有也可能没有尖叫着的小女生(或者小男生,我们不要作道学家或性别岐视)来敲你更衣室的门,作”负责人”会-给你带来巨大的个人成就感。不用说你的”领导”其实是拼命从后面追赶大家、努力跟上大家的步伐。每个人还是会认为你是”负责人”。

如果你能驾驭得了,这是一项伟大的工作。

参考:原文地址

汉字繁简体互换

1、chs2cht.h

#pragma once
char* UnicodeToBIG5(const wchar_t* szUnicodeString);
char* UnicodeToGB2312(const wchar_t* szUnicodeString);
wchar_t* GB2312ToUnicode(const char* szGBString);
char* GB2312ToBIG5(const char* szGBString);

2、chs2cht.cpp

#include "chs2cht.h"
#include <atlstr.h>
//GB2312 转 Unicode:
wchar_t* GB2312ToUnicode(const char* szGBString)  
{  
	UINT nCodePage = 936; //GB2312  
	int nLength=MultiByteToWideChar(nCodePage,0,szGBString,-1,NULL,0);  
	wchar_t* pBuffer = new wchar_t[nLength+1];  
	MultiByteToWideChar(nCodePage,0,szGBString,-1,pBuffer,nLength);  
	pBuffer[nLength]=0;  
	return pBuffer;  
}

//BIG5 转 Unicode:  
wchar_t* BIG5ToUnicode(const char* szBIG5String)  
{  
	UINT nCodePage = 950; //BIG5  
	int nLength=MultiByteToWideChar(nCodePage,0,szBIG5String,-1,NULL,0);  
	wchar_t* pBuffer = new wchar_t[nLength+1];  
	MultiByteToWideChar(nCodePage,0,szBIG5String,-1,pBuffer,nLength);  
	pBuffer[nLength]=0;  
	return pBuffer;  
}

//Unicode 转 GB2312:  
char* UnicodeToGB2312(const wchar_t* szUnicodeString)  
{  
	UINT nCodePage = 936; //GB2312  
	int nLength=WideCharToMultiByte(nCodePage,0,szUnicodeString,-1,NULL,0,NULL,NULL);  
	char* pBuffer=new char[nLength+1];  
	WideCharToMultiByte(nCodePage,0,szUnicodeString,-1,pBuffer,nLength,NULL,NULL);  
	pBuffer[nLength]=0;  
	return pBuffer;  
}

//Unicode 转 BIG5:  
char* UnicodeToBIG5(const wchar_t* szUnicodeString)  
{  
	UINT nCodePage = 950; //BIG5  
	int nLength=WideCharToMultiByte(nCodePage,0,szUnicodeString,-1,NULL,0,NULL,NULL);  
	char* pBuffer=new char[nLength+1];  
	WideCharToMultiByte(nCodePage,0,szUnicodeString,-1,pBuffer,nLength,NULL,NULL);  
	pBuffer[nLength]=0;  
	return pBuffer;  
}

//繁体中文BIG5 转 简体中文GB2312  
char* BIG5ToGB2312(const char* szBIG5String)  
{  
	LCID lcid = MAKELCID(MAKELANGID(LANG_CHINESE,SUBLANG_CHINESE_SIMPLIFIED),SORT_CHINESE_PRC);  
	wchar_t* szUnicodeBuff = BIG5ToUnicode(szBIG5String);  
	char* szGB2312Buff = UnicodeToGB2312(szUnicodeBuff);  
	int nLength = LCMapStringA(lcid,LCMAP_SIMPLIFIED_CHINESE, (LPCSTR)szGB2312Buff,-1,NULL,0);  
	char* pBuffer = new char[nLength + 1];  
	LCMapStringA(0x0804,LCMAP_SIMPLIFIED_CHINESE,(LPCSTR)szGB2312Buff,-1,pBuffer,nLength);  
	pBuffer[nLength] = 0;  

	delete[] szUnicodeBuff;  
	delete[] szGB2312Buff;  
	return pBuffer;  
}

//简体中文GB2312 转 繁体中文BIG5  
char* GB2312ToBIG5(const char* szGBString)  
{  
	LCID lcid = MAKELCID(MAKELANGID(LANG_CHINESE,SUBLANG_CHINESE_SIMPLIFIED),SORT_CHINESE_PRC);  
	int nLength = LCMapStringA(lcid,LCMAP_TRADITIONAL_CHINESE,szGBString,-1,NULL,0);  
	char* pBuffer=new char[nLength+1];  
	LCMapStringA(lcid,LCMAP_TRADITIONAL_CHINESE,szGBString,-1,pBuffer,nLength);  
	pBuffer[nLength]=0;  
	wchar_t* pUnicodeBuff = GB2312ToUnicode(pBuffer);  
	char* pBIG5Buff = UnicodeToBIG5(pUnicodeBuff);  
	delete[] pBuffer;  
	delete[] pUnicodeBuff;  
	return pBIG5Buff;  
}

3、test.cpp

#include "chs2cht.h"
#include <iostream> 
#include <locale.h>
#include <atlstr.h>  

using namespace std;

int main(int argc, char** argv)
{
    //“区域设置”为简体中文 
    locale loc( "chs" ); 
    char str[100];  
    cin>>str;  
    
    char * rlt=GB2312ToBIG5(str);  
    CString cStr1;   
    cStr1.Format( TEXT("%s"),rlt); 

    //“区域设置”为繁体中文 
    setlocale(LC_ALL, ".950");  
    cout<<rlt<<endl; 

    return 0;
}

关于汉字编码

关于汉字编码

  为进行信息交换,各汉字使用地区都制订了一系列汉字字符集标准。

  1、 GB2313字符集,收入汉字6763个,符号715个,总计7478个字符,这是大陆普遍使用的简体字符集。楷体-GB2313、仿宋-GB2313、华文行楷等市面上绝大多数字体支持显示这个字符集,亦是大多数输入法所采用的字符集。市面上绝大多数所谓的繁体字体,其实采用的是GB-2313字符集简体字的编码,用字体显示为繁体字,而不是直接用GBK字符集中繁体字的编码,错误百出。

  2、 BIG-5字符集,收入13060个繁体汉字,808个符号,总计13868个字符,目前普遍使用于台湾、香港等地区。台湾教育部标准宋体楷体等港台大多数字体支持这个字符集的显示。

  3、 GBK字符集,又称大字符集(GB=GuóBiāo国标,K=扩展),包含以上两种字符集汉字,收入21003个汉字,882个符号,共计21885个字符,包括了中日韩(CJK)统一汉字20902个、扩展A集(CJK Ext-A) 中的汉字52个。Windows 95\98简体中文版就带有这个GBK.txt文件。宋体、隶书、黑体、幼圆、华文中宋、华文细黑、华文楷体、标楷体(DFKai-SB)、Arial Unicode MS、MingLiU、PMingLiU等字体支持显示这个字符集。微软拼音输入法2003、全拼、紫光拼音等输入法,能够录入如镕镕炁夬喆嚞姤赟赟龑昳堃慜靕臹等GBK简繁体汉字。

  4、BIG-5 (繁体中文)与GB-2313 (简体中文),编码不相兼容,字符在不同的操作系统中便产生乱码。文本文字的简体与繁体(文字及编码)之间的转换,可用BabelPad、TextPro或Convertz之类的转码软件来解决。若是程序,Windows XP操作系统,可用Microsoft AppLocale Utility 1.0解决;Windows 2000的操作系统,大概只有用:中文之星、四通利方、南极星、金山快译之类的转码软件方能解决了。

  5、 GB18030字符集,包含GBK字符集、CJK Ext-A 全部6582个汉字,共计27533个汉字。宋体-18030、方正楷体(FZKai-Z03)、书同文楷体(MS Song)宋体(ht_cjk+)、香港华康标准宋体(DFSongStd)、华康香港标准楷体、CERG Chinese Font、韩国New Gulim,以及微软Windows Vista操作系统提供的宋黑楷仿宋等字体亦支持这个字符集的显示。Windows 98支持这个字符集,以下的字符集则不支持。手写输入法逍遥笔4.0版支持GB18030字符集及方正超大字符集汉字的录入。

  6、 方正超大字符集,包含GB18030字符集、CJK Ext-B中的36862个汉字,共计64395个汉字。宋体-方正超大字符集支持这个字符集的显示。Microsoft Office XP或2003简体中文版就自带有这个字体。Windows 2000的操作系统需安装超大字符集支持包“Surrogate更新”。

  7、 ISO/IEC 10646 / Unicode字符集,这是全球可以共享的编码字符集,两者相互兼融,涵盖了世界上主要语文的字符,其中包括简繁体汉字,计有:CJK统一汉字20902个,CJK Ext-A 6582个,Ext-B 42711个,共计70195个汉字。SimSun-ExtB(宋体)、MingLiU-ExtB(细明体)能显示全部Ext-B汉字。至今尚无单独一款字体能够显示全部70195个汉字,但可用海峰五笔、新概念五笔、仓颉输入法世纪版、新版的微软新注音、仓颉输入法 6.0 版(单码功能)等输入法录入。Ext-C还有2万多个汉字。详情请参阅香港中文大学网站、马来西亚仓颉之友网站、福建陈清钰个人网站。

  8、 汉字构形数据库2.3版,内含楷书字形60082个、小篆11100个、楚系简帛文字2627个、金文3459个、甲骨文177个、异体字12768组。可以安装该程序,亦可以解压后使用其中的字体文件,对于整理某些古代文献十分有用。

  如果超出了输入法所支持的字符集,就不能录入计算机。如果没有相应字体的支持,则显示为黑框、方框或空白。如果操作系统或应用软件不支持该字符集,则显示为问号(一个或两个)。在网页上亦存在同样的情况。

关于Unicode

  由于各国国家标准字集所收的汉字字数、常用字的差异,虽然象中国两岸GB/BIG5字集常用字基本类似,转换后阅读并不成问题,但是这种编码转换的混乱关系,对文字交流始终是一种障碍。因此相关国家的标准化组织和文字工作者经过共同努力,终于在93年完成了包含中日韩(CJK)汉字的Unicode 汉字标准ISO 10646.1。 Unicode是完全双字节表示的多国文字编码体系,编码空间0x0000-0xFFFF。 ISO 10646.1汉字标准使用编码0x4E00-9FA5,共包含20902个汉字。其中: 大陆(S)提出的汉字17124个,台湾(T)提出的汉字17258个; S与T的并集,即中国(C)提出的汉字为20158个。 日本(J)提出的汉字为12157个,中国未提出的690个(Ja); 韩国(K)提出的汉字为7477个,其中中国未提出的90个(Ka); Ja与Ka并集共744字。 支持Unicode编码的相关电脑系统软件,如Unix, Win95已有推出,但是由于Unicode的ASCII码是用双字节编码(即一般电脑系统中的单字节ASCII码前加 0x00),同时其汉字编码与各国的现有编码也不兼容,造成现有的软件和数据不能直接使用,所以目前完全使用Unicode软件系统的用户并不多,大多数只将它此作为一个国际语言编码标准来使用。

原文:汉字简介

我和Google Play Music的悲惨故事2

小米2S刷原生后,使用Google Play Music有个比较烦的问题,就是其默认存储路径在第一存储上(只有4G),第二存储(存储卡)上几十G都是空的。

终于,某一天第一存储爆了,但我不想删程序啊。

回头一想,nnd,Android不就是linux吗,这个简单了

1、将/data/data/com.google.android.music/files/music整个文件夹移
动到存储卡上/storage/sdcard0/googleplay/.hide/music上
2、用ln命令,在原位置创建一个连接
3、打开Google Play Music,一切正常,哈哈哈哈哈

第二天发现,Google Play Music把/storage/sdcard0/googleplay/.hide/music的音乐又加了一般,
而且没有Tag,整个一悲剧啊。

那就Google一下,发现在文件夹下创建.nomedia文件后,可以防止Google Play Music进行扫描。

很Happy的到music文件夹下去看看,发现.nomedia已经躺在那里好久了,这~~

死马当活马医了,那就在googleplay和.hide都增加一下.nomedia,然后重启

居然好了~~

好吧~~

就这么先用着吧~~

我和Google Play Music的悲惨故事1

去年入手了一款小米2S,回来第一时间刷成了原生Android4.1.1,取得Root权限。

后来慢慢发现Google Play Music这款工具不错,操作简便,还能同步Google音乐,于是就开始Happy的使用了。

出于尊重正版的单纯想法,买了200多首歌曲,除了没有歌词,感觉还不错。

但有个问题,Google Play Music没有IOS版本啊,这个坑爹啊。

我把音乐从手机拷贝出来,发现MP3的Tag一个都没有,这不是坑爹吗。

于是开始分析Google Play Music的存储方式:
1、mp3音频文件存在/data/data/com.google.android.music/files/music下面
2、mp3的封面文件存在/data/data/com.google.android.music/files/artwork下面
3、mp3的Tag信息,及存储路径存在/data/data/com.google.android.music/databases/music.db下面

好吧那就分析下music.db文件吧:
只有一张表是我关心的,就是MUSIC表

然后,当然是写程序搞定啊,用Java ID3 Tag Library 0.5.4(org.farng.mp3) + sqlite-jdbc-3.7.2两个包,
从MUSIC表读出Tag,然后将Tag和封面图片写入到新的.mp3文件中。

经过2小时奋战,处理了JDBC无法连接和TAG乱码两个问题,搞定。

然后放到iTunes中,同步。

然后带封面的mp3就可以用了。

哈哈哈哈哈,开心了好几天。

后来发现,Google可以在线下载,但只能下两次。

再后来发现,Google出了Music Manager,虽然上传下载的都会经常中断,但有一个好处,
那就是我自己捣腾的mp3,用第三方软件无法找到歌词,但用Music Manager导出的软件,用第三方软件可以找到歌词。

悲剧啊,浪费了好几个小时~~

JS编辑器研究

最近项目需要用到JS编辑器,找了一些开源项目看了下(由于客户浏览器版本不高,HTML5版本几乎无法使用),
就找到了下面三个比较靠谱一点的编辑器:

nicEdit 无jQuery 小巧,易于集成,功能有限
elrte 有jQuery 功能强大,易于集成,但很久没更新了
ckeditor 有jQuery 功能强大,更新给力,易集成性貌似差一些

回头找找看,还有没有其他靠谱的工具。