博客

  • 2019-11-20-how-to-read-a-book

    How to read a book

    abstract

    1. Read the whole thing!
      Major arguments and evidence matter more than details. Grasping the structure of the whole is more important than reading every word.
    2. Decide how much time you will spend!
      Real-world time is limited. If you know exactly how long you can actually spend on reading, you can plan how much time to devote to each item.
    3. Have a purpose and a strategy!
      You’ll enjoy reading more, and remember it better, if you know exactly why you’re reading.
    4. Read actively!
      Never rely on the author’s structures alone. Move around in the text, following your own goals.
    5. Read it three times!
      First time for overview and discovery. Second time for detail and understanding. Third time for note-taking in your own words.
    6. Focus on parts with high informaion content!
      Tables of contents, pictures, charts, headings, and other elements contain more information than body text.
    7. Use personal text markup language!
      Mark up your reading with your own notes. This helps you learn and also helps you find important passages later.
    8. Know the author and organizations!
      Authors are people with backgrounds and biases. They work in organizations that give them context and depth.
    9. Know the intellectual context!
      Most academic writing is part of an ongoing intellectual conversation, with debates, key figures, and paradigmatic concepts.
    10. Use your unconscious mind!
      Leave time between reading sessions for your mind to process the material.
    11. Rehearse and use multiple modes!
      Talking, visualizing, or writing about what you’ve read helps you remember it.

    Full text:

    How to Read a Book, v5.0
    Paul N. Edwards
    University of Michigan School of Information
    pne.people.si.umich.edu

    This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. The terms of this licence allow you to remix, tweak, and build upon this work non-commercially, as long as you credit me and license your new creations under the identical terms. Quasi-permanent URL: pne.people.si.umich.edu/PDF/howtoread.pdf

    How can you learn the most from a book — or any other piece of writing — when you’re reading for information, rather than for pleasure?

    It’s satisfying to start at the beginning and read straight through to the end. Some books, such as novels, have to be read this way, since a basic principle of fiction is to hold the reader in suspense. Your whole purpose in reading fiction is to follow the writer’s lead, allowing him or her to spin a story bit by bit.

    But many of the books, articles, and other documents you’ll read during your undergraduate and graduate years, and possibly during the rest of your professional life, won’t be novels. Instead, they’ll be non-fiction: textbooks, manuals, journal articles, histories, academic studies, and so on.

    The purpose of reading things like this is to gain, and retain, information. Here, finding out what happens — as quickly and easily as possible — is your main goal. So unless you’re stuck in prison with nothing else to do, NEVER read a non-fiction book or article from beginning to end.

    Instead, when you’re reading for information, you should ALWAYS jump ahead, skip around, and use every available strategy to discover, then to understand, and finally to remember what the writer has to say. This is how you’ll get the most out of a book in the smallest amount of time.

    Using the methods described here, you should be able to read a 300-page book in six to eight hours. Of course, the more time you spend, the more you’ll learn and the better you’ll understand the book. But your time is limited.

    Here are some strategies to help you do this effectively. Most of these can be applied not only to books, but also to any other kind of non-fiction reading, from articles to websites. Table 1, on the next page, summarizes the techniques, and the following pages explain them in more detail.

    Read the whole thing!

    In reading to learn, your goal should always be to get all the way through the assignment. It’s much more important to have a general grasp of the arguments or hypotheses, evidence, and conclusions than to understand every detail. In fact, no matter how carefully you read, you won’t remember most of the details anyway.

    What you can do is remember and record the main points. And if you remember those, you know enough to find the material again if you ever do need to recall the details.

    Table 1. Summary of reading strategies and techniques

    Strategies and techniquesRationale
    Read the whole thingMajor arguments and evidence matter more than details. Grasping the structure of the whole is more important than reading every word.
    Decide how much time you will spendReal-world time is limited. If you know exactly how long you can actually spend on reading, you can plan how much time to devote to each item.
    Have a purpose and a strategyYou’ll enjoy reading more, and remember it better, if you know exactly why you’re reading.
    Read activelyNever rely on the author’s structures alone. Move around in the text, following your own goals.
    Read it three timesFirst time for overview and discovery. Second time for detail and understanding. Third time for note-taking in your own words.
    Focus on parts with high information contentTables of contents, pictures, charts, headings, and other elements contain more information than body text.
    Use PTML (personal text markup language)Mark up your reading with your own notes. This helps you learn and also helps you find important passages later.
    Know the author(s) and organizationsAuthors are people with backgrounds and biases. They work in organizations that give them context and depth.
    Know the intellectual contextMost academic writing is part of an ongoing intellectual conversation, with debates, key figures, and paradigmatic concepts.
    Use your unconscious mindLeave time between reading sessions for your mind to process the material.
    Rehearse, and use multiple modesTalking, visualizing, or writing about what you’ve read helps you remember it.

    Decide how much time you will spend

    If you know in advance that you have only six hours to read, it’ll be easier to pace yourself. Remember, you’re going to read the whole book (or the whole assignment).

    In fact, the more directly and realistically you confront your limits, the more effective you will be at practically everything. Setting time limits and keeping to them (while accomplishing your goals) is one of the most important life skills you can learn. So never start to read without planning when to stop.

    Have a purpose and a strategy

    Before you begin, figure out why you are reading this particular book, and how you are going to read it. If you don’t have reasons and strategies of your own — not just those of your teacher — you won’t learn as much.

    As soon as you start to read, begin trying to find out four things:

    • Who is the author?
    • What are the book’s arguments?
    • What is the evidence that supports these?
    • What are the book’s conclusions?

    Once you’ve got a grip on these, start trying to determine:

    • What are the weaknesses of these arguments, evidence, and conclusions?
    • What do you think about the arguments, evidence, and conclusions?
    • How does (or how could) the author respond to these weaknesses, and to your own criticisms?

    Keep coming back to these questions as you read. By the time you finish, you should be able to answer them all. Three good ways to think about this are:

    1. Imagine that you’re going to review the book for a magazine.
    2. Imagine that you’re having a conversation, or a formal debate, with the author.
    3. Imagine an examination on the book. What would the questions be, and how would you answer them?

    Read actively

    Don’t wait for the author to hammer you over the head. Instead, from the very beginning, constantly generate hypotheses (“the main point of the book is that…”) and questions (“How does the author know that…?”) about the book.

    Making brief notes about these can help. As you read, try to confirm your hypotheses and answer your questions. Once you finish, review these.

    Know the author(s) and organizations

    Knowing who wrote a book helps you judge its quality and understand its full significance. Authors are people. Like anyone else, their views are shaped by their educations, their jobs, their early lives, and the rest of their experiences. Also like anyone else, they have prejudices, blind spots, desperate moments, failings, and desires — as well as insights, brilliance, objectivity, and successes. Notice all of it.

    Most authors belong to organizations: universities, corporations, governments, newspapers, magazines. These organizations each have cultures, hierarchies of power, and social norms. Organizations shape both how a work is written and the content of what it says. For example, university professors are expected to write books and/or journal articles in order to get tenure. These pieces of writing must meet certain standards of quality, defined chiefly by other professors; for them, content usually matters more than good writing. Journalists, by contrast, are often driven by deadlines and the need to please large audiences. Because of this, their standards of quality are often directed more toward clear and engaging writing than toward unimpeachable content; their sources are usually oral rather than written.

    The more you know about the author and his/her organization and/or discipline, the better you will be able to evaluate what you read. Try to answer questions like these: What shaped the author’s intellectual perspective? What is his or her profession? Is the author an academic, a journalist, a professional (doctor, lawyer, industrial scientist, etc.)? Expertise? Other books and articles? Intellectual network(s)? Gender? Race? Class? Political affiliation? Why did the author decide to write this book? When? For what audience(s)? Who paid for the research work (private foundations, government grant agencies, industrial sponsors, etc.)? Who wrote “jacket blurbs” in support of the book?

    You can often (though not always) learn about much of this from the acknowledgments, the bibliography, and the author’s biographical statement.

    Know the intellectual context

    Knowing the author and his/her organization also helps you understand the book’s intellectual context. This includes the academic discipline(s) from which it draws, schools of thought within that discipline, and others who agree with or oppose the author’s viewpoint.

    A book is almost always partly a response to other writers, so you’ll understand a book much better if you can figure out what, and whom, it is answering. Pay special attention to points where the author tells you directly that s/he is disagreeing with others:

    “Conventional wisdom holds that x, but I argue instead that y.” (Is x really conventional wisdom? Among what group of people?) “Famous Jane Scholar says that x, but I will show that y.” (Who’s Famous Jane, and why do other people believe her? How plausible are x and y? Is the author straining to find something original to say, or has s/he genuinely convinced you that Famous Jane is wrong?) Equally important are the people and writings the author cites in support of his/her arguments.

    Read it three times

    This is the key technique. You’ll get the most out of the book if you read it three times — each time for a different purpose.

    a) Overview: discovery (5-10 percent of total time)

    Here you read very quickly, following the principle (described below) of reading for high information content. Your goal is to discover the book. You want a quick-and-dirty, unsophisticated, general picture of the writer’s purpose, methods, and conclusions.

    Mark — without reading carefully — headings, passages, and phrases that seem important (you’ll read these more closely the second time around.) Generate questions to answer on your second reading: what does term or phrase X mean? Why doesn’t the author cover subject Y? Who is Z?

    b) Detail: understanding (70-80 percent of total time)

    Within your time constraints, read the book a second time. This time, your goal is understanding: to get a careful, critical, thoughtful grasp of the key points, and to evaluate the author’s evidence for his/her points.

    Focus especially on the beginnings and ends of chapters and major sections. Pay special attention to the passages you marked on the first round. Try to answer any questions you generated on the first round.

    c) Notes: recall and note-taking (10-20 percent of total time)

    The purpose of your third and final reading is to commit to memory the most important elements of the book. This time, make brief notes about the arguments, evidence, and conclusions. This is not at all the same thing as text markup; your goal here is to process the material by translating into your own mental framework, which means using your own words as much as possible. Cutting and pasting segments of text from the book will not do as much for you as summarizing very briefly in your own words. Include the bare minimum of detail to let you remember and re-locate the most important things. 1-3 pages of notes per 100 pages of text is a good goal to shoot for; more than that is often too much. Use some system that lets you easily find places in the book (e.g., start each note with a page number.)

    Notebooks, typed pages, or handwritten sheets tucked into the book can all work. However, notes will be useless unless you can easily find them again. A very good system — the one I use — is to type notes directly into bilbiography entries using citation manager software such as Endnote, Zotero, or Bookends. See below for more on citation managers.

    On time and timing

    First, because human attention fades after about an hour, you’ll get more out of three onehour readings than you could ever get out of one three-hour reading. But be careful: to get one full hour of effective reading, you need to set aside at least one hour and fifteen minutes, since distraction is inevitable at the beginning (settling in) and end (re-arousal for your next task) of any reading period.

    Second, make a realistic plan that includes how much time you will devote to each of the three stages. For a 250-page book, I might spend 15 minutes on overview, 4 hours on detailed reading, and 20-30 minutes making notes — but I’d adjust these periods up or down depending on how difficult the text is, how important it is to me, and how much time I have.

    Focus on the parts with high information content

    Non-fiction books very often have an “hourglass” structure that is repeated at several levels of organization. More general (broader) information is typically presented at the beginnings and ends of:

    • the book or article as a whole (abstract, introduction, conclusion)
    • each chapter
    • each section within a chapter
    • each paragraph
      More specific (narrower) information (supporting evidence, details, etc.) then appears in the middle of the hourglass.

    The Hourglass Information Structure: General -> Specific -> General

    You can make the hourglass structure of writing do a lot of work for you. Focus on the following elements, in more or less the following order:

    • Front and back covers, inner jacket flaps
    • Table of contents
    • Index: scan this to see which are the most important terms
    • Bibliography: tells you about the book’s sources and intellectual context
    • Preface and/or Introduction and/or Abstract
    • Conclusion
    • Pictures, graphs, tables, figures: images contain more information than text
    • Chapter introductions and conclusions
    • Section headings
    • Special type or formatting: boldface, italics, numbered items, lists

    Use PTML (personal text markup language)

    Always, always, always mark up your reading. This is a critical part of active reading. Do this from the very beginning — even on your first, overview reading. Why? Because when you come back to the book later, your marks reduce the amount you have to look at and help you see what’s most significant.

    Don’t mark too much. This defeats the purpose of markup; when you consult your markup later, heavy markup will force you to re-read unimportant information. As a rule, you should average no more than two or three short marks per page. Rather than underline whole sentences, underline words or short phrases that capture what you most need to remember. The point of this is to distill, reduce, eliminate the unnecessary. Write words and phrases in the margins that tell you what paragraphs or sections are about. Use your own words.

    Page vs. screen

    Printed material has far higher resolution (~600 dpi) than even the best computer screens (~100 dpi); see the illustration of 300 vs. 600 dpi, below. For this reason you will read more accurately, and with less fatigue, if you stick with the paper version. Still, we inevitably read much more screen-based material now.

    Markup on the screen: It remains difficult to mark up screen-based materials effectively.

    The extra steps involved are distracting, as is the temptation to check email or websurf. Also, with screen-based markup you often have to click on a note in order to read it, which means you’re less likely to do it later. It remains far easier to mark up a printed copy! However, if you’re disciplined, recent versions of Acrobat, Apple Preview, and third-party PDF viewers such as PDFpen, iAnnotate, and Goodreader allow you to add comments, highlighting, and so on to PDFs. Voice recognition can make this a lot easier. Today, I routinely read and annotate PDFs on an iPad, using voice recognition when I want to make a note. Some of these readers, as well as ebook readers such as Kindle, allow you to export only your highlights and notes. This is a great way to make yourself a condensed version of a document. Paste it into the notes field of your citation manager and it’ll always be at your fingertips. Hunt around on the web for ways to do this kind of thing on an industrial scale (especially with Kindle books).

    When taking notes about something you’re reading (as opposed to marking up the text), you’ll be tempted to cut and paste the original text in lieu of making your own notes in your own words. Cut-and-paste can sometimes work well, especially for things you might want to quote later. However: in general it defeats the two main purposes of note-taking: (a) learning and remembering (by rephrasing in your own terms), and (b) condensing into a very short form. The same is true of links: though useful for keeping track of sources, keeping a URL will not by itself help you remember or understand what’s there, even though it may feel that way.

    Use a citation manager

    It’s hard to overemphasize the huge advantages of citation manager software such as Endnote, Bookends, Zotero, Mendeley, CiteULike, etc. They let you keep track of your growing library, easily enter and format citations in your word processor (saving you the incredible irritation of doing it yourself). Most of them can pull in citations directly from the web, record web links, find DOI’s, and so on. Some have their own web search tools built in. Some, such as Bookends (Mac only), will automatically rename documents with AuthorDate-Title, a huge help with the extremely annoying problem of uninformative filenames.

    None of these packages are perfect. All have both advantages and disadvantages, and the more sophisticated ones have steep learning curves. Look for one that can handle all major document formats, including books, journal articles, newspaper articles, online sources, interviews, and so on. Be wary of managers that only handle PDFs, since so many other formats are still important.

    If you use the notes field of your citation manager in a disciplined way, your notes will always be easy to find. When your library starts reaching into the thousands of items, this is a godsend.

    Use your unconscious mind

    An awful lot of thinking and mental processing goes on when you’re not aware of it. Just as with writing or any other creative thought process, full understanding of a book takes time to develop.

    Like the body, the mind suffers from fatigue when doing just one thing for many hours. Your ability to comprehend and retain what you read drops off dramatically after an hour or so. Therefore, you should read a book in several short sessions of one to two hours apiece, rather than one long marathon.

    In between, your unconscious mind will process some of what you’ve read. When you come back for the next session, start by asking yourself what you remember from your previous reading, what you think of it so far, and what you still need to learn.

    Rehearse, and use multiple modes

    Reading is exactly like martial arts, baseball, or cooking in the sense that learning and memory depend crucially on rehearsal.

    So — after you’ve read the book, rehearse what you’ve learned. Quiz yourself on its contents. Argue with the author. Imagine how you would defend the author’s position in your own writing.

    Reading, writing, speaking, listening, and visualizing all engage different parts of the brain. For this reason, the best forms of rehearsal use multiple modes of thinking and action. Don’t just contemplate privately. Instead, talk about the book with others. Bring it up in classes. Write about it. Visualize anything that can be visualized about its contents. All of this helps fix your memory and integrate your new learning into the rest of your knowledge.

    Hang in there!

    When I give presentations on these ideas, students often tell me a few weeks later that they “tried it a few times and just couldn’t do it,” so they stopped.

    You will have to practice these techniques for a considerable length of time — at least a few months — before they come to seem natural, and they will never be easier than the comfortable, passive way we’ve all been reading for many years.

    Hang in there. The rewards of these techniques are great, or so say the hundreds of students who’ve told me so years later. Learning to read like this can be a critical key to a successful career as a student, scholar, or professional in almost any field.

  • 2020-07-02-培养渴望

    原文 Cultivating Burning Desire https://www.stevepavlina.com/blog/2005/03/cultivating-burning-desire/

    在接受采访时被问到他是如何成功成为职业健美运动员和好莱坞演员的时候,阿诺德·施瓦辛格回答了一个单词:“驱使!”所有伟大的成功最终都源于一个想法,但是使想法变为现实的是人类渴望的动力。一个想法本身可以带给您暂时的灵感感觉,但是强烈的欲望却使您度过了克服沿途不可避免的障碍所需的全部汗水。

    花点时间考虑一下您为自己设定的目标。 (您已经设定了目标,不是吗?如果没有,请阅读有关设定明确目标的文章。https://www.stevepavlina.com/blog/2005/03/the-power-of-clarity/)您对实现这些目标的决心如何?您会在什么条件下放弃?如果您可以极大地提高实现这些目标的愿望,应该怎么办?如果您如此迫切地想要他们,以至于您可以绝对肯定地知道自己绝对,永远不会放弃,应该怎么办?当您真正100%致力于实现自己的目标时,您就会从希望变为了解。如果您非常想得到一些东西,那么退出是根本不可行的。您要么找到方法,要么就创造方法。您付出代价,无论什么代价。

    那些强烈渴望实现目标的人通常被称为“推动者”。但是,这种特殊的品质仅保留给特权人士吗?当然不是。有了正确的方法,任何人都可以在自己内心深处激起强烈的渴望,并朝着完全投入的状态迈进,并肯定地知道成功就像日出一样不可避免。

    那么,如何培养强烈的欲望呢?您从外而内的方法开始,以改变环境的方式来增强您的决心,同时消除疑虑。如果您花时间做正确的事,您将建立一个积极的反馈周期,这样您的欲望将每天持续增加。

    您可以采取以下八个步骤来培养强烈的愿望,以实现自己设定的目标:

    1.破釜沉舟

    我不会为此打气。如果您的目标对您确实足够重要,那么您可以从焚毁众所周知的退路之船开始,这样您别无选择,只能继续前进。例如,如果您想开办自己的企业,则可以从承诺辞职开始。写下辞职信,放到给老板的邮票盖章信封中,然后将其发给值得信赖的朋友,上面有明确的指示,如果您在某个日期之前还没有辞职的话,可以邮寄这封信。

    一位拉斯维加斯赌场经理做出了戒烟的决定。他不觉得自己有个人意愿去做,所以他在拉斯维加斯大道上拿出一个广告牌,上面贴着照片,上面写着:“如果你让我吸烟,我会给你10万美元! ”他能戒烟吗?你打赌! (好吧,双关语。)这称为意志力杠杆。您会使用一点意志力来确定结果,这实际上会迫使您信守承诺。正如安德鲁·卡内基(Andrew Carnegie)曾经说过的那样:“将所有鸡蛋放在一个篮子里,然后看着那个篮子!”

    孙子在经典著作《孙子兵法》中指出,士兵们以为自己要死而战时,战斗最为激烈。一个好将军知道,在攻击敌方部队时,重要的是要给敌人创造一个潜在的逃生路线的幻觉,(围三缺一)这样他们就不会死战到底。您一直在打开哪些逃生路线,这些战斗导致您不那么努力奋斗?

    如果您不焚烧这些船只,那么您是在向潜意识发送一条消息,那就是可以退出。当事情变得艰难时,正如它不可避免地为实现任何有价值的目标所做的那样,您将退出。如果您真的想实现自己的目标,那么就必须将那些船烧成灰,并撒下灰烬。如果您认为普通人不会这样做,那么您是对的-这就是为什么他们是普通人。

    2.用欲望增强器充实您的环境

    假设您的重要目标之一就是减肥。找一些海报板,制作自己的海报,上面写着“我体重X磅”,其中X是您的目标体重,然后将它们放在您的房屋周围。将您的屏幕保护程序更改为显示相同内容的文本消息(或更改为同样具有启发性的图像)。获取一些杂志,剪出与您想要的身体相似的人的照片,然后将它们放在您的房子周围。剪出对您有益的健康食品图片,并将其张贴在厨房周围。如果您在办公室工作,请以相同方式更改办公室。不必担心您的同事会怎么想,只要做到!他们起初可能会嘲笑你,但他们也将开始了解您的决心。

    3.与积极的人相处

    与会鼓励您迈向目标的人们交朋友,并找到与他们共度时光的方法。仅与支持您的人分享您的目标,而不与那些冷嘲热讽或冷漠的人分享您的目标。例如,如果您想减肥,请进入健身房,并开始与身体健康的人成为朋友。您会发现他们的态度具有传染性,并且开始相信自己也可以做到。与体重减轻一百磅或更多的人见面可能会非常有动力。如果您想开展新业务,请加入当地的商会或行业协会。尽一切努力结交新朋友,以帮助您保持承诺。

    尽管这对于某些人来说可能很困难,但您还需要从生活中远离那些消极的人。我曾经读过一篇文章,您只需要看看与您在一起时间最多的六个人,便可以看到自己的未来。如果您不喜欢看到的内容,请更改这些人。忠于那些期望您失败的人,这是没有荣誉的。例如,人们无法创办自己的企业的原因之一是,他们将大部分时间都花在与其他员工的交往上。摆脱这种陷阱的方法是开始花费更多的时间与企业主建立联系,例如加入贸易协会。心态具有传染性。因此,请与值得关注的人一起度过时光。

    4.每天根据增强的信息来充实您的思想

    励志书籍和音频程序是培养欲望的最佳动力之一。如果您想戒烟,请阅读由前吸烟者写的一本有关戒烟的书。如果您想创业,那就开始吞噬商业书籍。有时去研讨会。我建议您每天至少十五分钟用某种形式的激励材料(书,文章,音频程序)来满足您的想法。这将不断为您的电池充电,并使您的欲望无比强大。

    当您吸收由一个非常热情的人创造的材料时,您常常会发现自己也变得更加热情。推荐一本伟大的书,星巴克的首席执行官霍华德·舒尔茨写的《你的心灵倾进去》(http://www.amazon.com/exec/obidos/ASIN/1565112334/dexteritysoft-20) 。当我阅读本书时,我对某人对咖啡如此狂热而感到非常惊讶。我强烈推荐的其他培养热情的作者/演说家是Harvey Mackay和Zig Ziglar。

    5.用正能量替代负能量源

    盘点生活中影响您态度的所有感官输入-您阅读的内容,在电视上观看的内容,房屋的清洁度等。请注意哪些输入会对您产生负面影响,并努力用积极的输入代替它们。我会给你一些好的起点。首先,避免看电视新闻-绝大多数是负面的。您是否真的需要听听那个被邻居的狗伤残的女人?用积极的投输入来充实这段时间,例如激励性和教育性音频程序。如果您喜欢看电影,请看充满正能量的电影,例如轻松喜剧和战胜逆境的故事。避免使您事后感到空虚的黑暗的悲剧电影。丢弃恐怖书籍,并以幽默书籍代替。花更多的时间在笑,而更少的时间在担心。如果您的桌子凌乱,请收拾干净!如果您有小孩或孙子,请花一些时间与他们一起玩。其中有些听起来有些老套,但确实有助于增加您的整体动力。如果您很难激励自己,可能是您的生活中充满了太多消极因素。幸福地实现远远好于实现了才觉得幸福。

    6.为成功着装

    每当您经过一面镜子(可能一天要经过几次)时,都会得到即时的图像增强效果。那么,您目前正在强化什么形象?如果您的目标已经实现,您的穿着会有所不同吗?您会选择其他发型吗?您会多洗一次澡吗?

    尽管多年来我一直喜欢破烂的牛仔裤和T恤的外观,但我注意到当我将来实现自己的目标并实现某些目标时,我穿得更加漂亮。通过一些实验,我发现了一种看起来专业且舒适的服装样式。因此,我逐渐将旧衣服捐赠给慈善机构,并用适合我正在成长的新身份的衣服替换了衣柜。(因此,有一家Salvation Army商店,里面存放着大量的游戏行业T恤。)我从一个前海军海豹队员那里学到了这个主意,他向我强调了以自己的外表为荣的重要性,我可以肯定地说它带来了显着的变化。因此,请确保您每天穿的衣服与您的新自我形象保持一致。

    7.使用心理编程

    这是一种神经语言编程(NLP)技术,可帮助您将强烈的积极情绪与正在努力实现的目标联系起来。寻找一些真正能激发您灵感的音乐。戴上耳机并聆听15到20分钟,这样做时,您便可以清晰地看到自己已经取得了想要的结果。使图像大,亮,生动,彩色,三维,全景和动画。生动地描绘场景,就像用自己的眼睛看一样(这很重要)。这将帮助您在音乐引起的积极情绪与您想要实现的目标之间形成神经联系,从而增强您的欲望。这是每天开始的好方法,如果您是在前一天晚上设置的东西,甚至可以在第一次醒来时躺在床上做。您应该定期循环播放音乐,因为如果您每次听相同的歌曲,所获得的情感冲动往往会减少。

    请记住,广告已经在使用这种形式的思维编程。观看快餐电视广告,您会注意到食物大,明亮,充满生气-汉堡旋转,生菜飞过水溅,切成成熟的西红柿-别忘了朗朗上口的曲调。因此,不要让别人为您制定自己的愿望,而要自己负责并在心理上重新编程。

    8.立即采取行动

    为自己设定目标后,立即采取行动。当您开始为新的目标而努力时,不必太担心制定详细的长期计划。人们常常陷入分析瘫痪状态,而从未达到行动阶段。您可以稍后制定计划,但请先行动。只需确定您首先需要采取的身体动作,然后再执行即可。例如,如果您决定减肥,请直接到冰箱里,扔掉所有垃圾食品。不用考虑也不要考虑后果。马上就做。

    成功的秘诀之一是认识到动力紧随行动。持续行动的动力助长了动力,而拖延则扼杀了动力。因此,大胆行事,好像不可能失败。如果您继续为自己的欲望加油,那么您将知道自己永远不会退出,而最终的成功将只是时间问题。

    如果您采用这八种策略,您将为您的火力永不燃尽的愿望加油。您将朝着目标前进,就像制导导弹到达目标一样,并且您会享受整个过程,因为您将专注于积极的回报而不是任务的难度。如果您有足够的正能量流入您体内,那么很快您就会获得正结果。这样,您很快就会成为其他人称为“驱动者”的人。

  • 2020-06-18-关于皇帝

    群里面提到了皇帝的话题,我就发表了一下意见。

    朱元璋是我最讨厌的一个皇帝。杀功臣,海禁,废除宰相,活人殉葬。

    真不能让农民当皇帝。

    本朝太祖其实也是农民。

    杀功臣杀的最狠的应该就是朱元璋了。

    我第二讨厌的就是太祖,还是那个原因,真不能让农民当皇帝。

    太祖读的是二十四史这种权谋的线装书,现代书读太少,经济学完全是外行。

    第三讨厌的是清太祖,因为降低中国人的人格,所有人都变成了奴隶。

  • 2020-06-14-关于安慰剂

    最近我在看一本书《上帝笑了99次》,里面提到了安慰剂的问题。我有点没想明白,就在本科群里做了发言。

    各位兄弟姐妹,我被一个问题给整蒙了。这里求助一下大家。

    我听到一个小故事,说你去医院看病,拿完药之后经过医生办公室,听见护士问医生,为什么刚才开的药这么便宜?医生说,那个药是安慰剂,没有什么有效成分。护士说,那能管用吗?医生说,只要病人相信那个药管用,它就管用,而且其他病人吃了都管用。

    现在的问题是那个安慰剂管用吗?

    我知道科学上已经证明安慰剂是有用的。很多药的临床实验里面,安慰剂甚至比测试的药品还管用。

    关键就是这个安慰剂起作用的原因,就是因为你相信它管用。我突然感觉这很不科学。因为我们做物理实验,不管你信不信,结果都一样。为什么在安慰剂这事儿上面,你信不信,结果就不一样呢?

    真正有效的药物不管你相不相信都起作用。

    我不能接受一个东西,只有相信它才能起作用。因为如果一个东西只有相信它,才起作用。就无法证明它到底起不起作用。

    比如把安慰剂换成上帝,当初有些基督徒跟我传教的时候就说,你一定要信上帝,因为信上帝,上帝就起作用,带你永生,不信他,你就下地狱。

    我当时就说这是扯淡。因为我不相信一个东西只有你信它,它才起作用。

    如果你能接受一个东西,只有信他才起作用,那我能很好的向你证明上帝存在。

    首先你要信上帝起作用,然后就没有了。

    因为分两种情况,一种你信上帝,那就不用证明了。另外一种情况你不信上帝,那就无法证明了,因为上帝是你信他,他才起作用。

    可能我没有说明白。简化一点,如果有人跟你说,你信上帝,上帝就存在。如果你不信上帝,上帝就不存在。

    那上帝到底存不存在呢?

    结论就是,对有些人上帝存在,对有些人上帝不存在。

    我不能接受的就是这一点,个人感觉一个东西存不存在应该与人无关,要么就存在,要么就不存在。

    朋友:你信安慰剂,安慰剂就存在。你不信,他也存在。

    嗯,不错,存在跟起作用是不一样的。

    不过我换个说法。你相信一个药有安慰剂作用,这个作用就存在,你不相信这个药有安慰剂作用,这个作用就不存在。

    但是安慰剂作用,这好像是一个结果,而不是一个实体。

    感觉安慰剂作用与上帝这两个概念中间有差别,但是一时想不明白差别在哪里。

    朋友:人体很复杂,其实不光是人,任何生物都很复杂,现在的物理化学根本解释不了这些复杂性问题。因为发现夸克得诺奖的人就觉得物理解释复杂性问题没有出路,改行了。

    这段话稍微让人心安一点。可能我们根本没有搞明白,我相信这个药有作用,这句话是什么意思。

    简单一点说,我相信这个药有作用,就会在我们身体里面产生一些变化,是这些变化导致了我们所得的病好了。

    导致我们病好的,其实是身体里面的那些变化,虽然目前我们还没有完全搞懂。

    这么一想的话,其实可以把相不相信这个因素给去掉了。

    相信安慰剂就相当于在你身体里面注射了一种特殊的药,这种药能治你的病。

    不相信安慰剂就相当于你没有注射这种药,没有这种药就治不了你的病。

    理论上说,如果你不相信安慰剂有作用,但是你能找到那种特殊的药,注射到你身体里面去也能治那种病。只不过目前技术还没有办法找到那种特殊的药。而且那种特殊的药可能针对不同的病产生的东西不一样。

    这样就没有矛盾了。归根到底跟你信不信,其实没有太大关系。只跟你身体没有没有产生那种变化有关系。稍微特殊一点的地方就在于,身体里面产生那种变化的前提是你相信那个安慰剂确实有用。

  • 2020-06-13-关于言论自由

    看了一篇文章管控辨析,
    https://mp.weixin.qq.com/s/fpcgSKHq3YW-sg8mNm037g

    下面是我想到的。

    天赋零权。有的时候我在想那些相信转基因有害的人,他们有言论自由到底是好事还是坏事?一方面,嘴长在他们身上,说话是他们的权利,所以言论自由似乎是天生的权利,不应该干涉。注意,我觉得言论自由就到此为止。你有说的权利,但是别人没有听的义务。另一方面,考虑到决策层也是人,而且很有可能会考虑这些大多数人的想法,最后做出错误的决定,所以限制他们的言论似乎也是合理的。

    以前我支持言论自由,就是因为我觉得愚者千虑,必有一得。那些人提出的1000条意见里面,哪怕只有一条靠谱,为了这一条能够被听见,我们也应该允许所有的1000条意见都被表达出来,从中筛选那一条是精英的责任。但是看了这篇文章,我想到所谓的精英也是人,他们有可能不但没有筛选出那一条靠谱的意见,反而被那999条错误意见所裹挟,从而做出错误的决策。这种情况在民主制度下更有可能发生。但即使是在中国这种环境,那些决策者也很难忽视那96%的人的错误意见。更要命的是,那些决策者并不都是精英,实际情况是那些决策者里面也只有4%是靠谱的。蠢货当政不仅可能,而且是经常发生的。

    这样想下来,真没有什么最合理的办法。最理想的情况是,真正的精英,就像柏拉图主张的那种哲人王当政,由他们来掌控言论自由。现实情况可能是相反的,一些蠢货控制了言论,那些错误的反制的主张反而占了主流。

  • 2020-06-05-关于比特币

    一个微信群里面聊到了比特币,下面是我的发言。

    新的一天到了。我想聊聊群群主说的这个btc话题。

    先说结论,我不同意BTC是老庄股的看法。我自己是从2011年接触到比特币的,我当时用笔记本的显卡挖了两个比特币,后来陆续买了几十个比特币,一直持有到现在。

    所以我想从一个比特币普通玩家的角度来说说我怎么看比特币。我不代表任何人,只代表自己。我当时买比特币的时候,出于两个原因,第一个原因是觉得很酷,因为比特币是程序员基于数学创造出来的不依赖第三方的自由货币。第二个原因就是转账很方便,因为我当时从新加坡往中国汇款有点麻烦,而且手续费也不低。

    我一直持有比特币也是有两个原因,一个原因是不缺钱,另外一个原因是它的价格一直在上涨。从比特币的链上数据可以看到很多人是跟我一样的,买入之后基本上就没动过它了。

    最后说说我对比特币的看法。这个看法也分两个方面。一方面,我觉得比特币是一个分布式的机构的股份,这个机构是由全世界的矿工,玩家,开发者,甚至有一部分金融机构组成的。由于这个机构一直在不断的扩大,比特币的总量又有限,所以这个股份的法币价格就一直在升高。矿工玩家和金融机构都是用自己的真金白银投入这个机构的,开发者是用自己的劳动投入这个机构的。所以我不同意它是一个老庄股,因为这么多人太难做庄了。

    另一方面,我觉得比特币是一个财富存储工具,可能是目前世界上唯一一个不贬值的财富存储工具。如果看过未来简史就知道,我们现有的很多东西都是基于想象的,比如公司国家都是想象的共同体,美元和各个国家的法币之所以有价值,就是因为法币在那些想象的共同体里面受到承认。在这方面比特币跟各种法币没有区别,只要有越来越多的人加入这个承认比特币的这个共同体,由于网络效应和数学上保证它总量有限,单个比特币就会升值。这样一来,你现在买的比特币将来就不会贬值。所以比特币就成了一个完美的价值储存工具,因为它不断不贬值,而且未来极有可能会升值,至少过去11年都是这样子的。这个系统已经稳定地运行了11年,我们有理由还能继续运行下去。当你把比特币看成一个价值存储工具,你就不会再那么纠结于它的法币价格变化了。

    最后我说一说对于投资比特币的看法。首先,比特币还是一个实验,谁也无法保证实验将来会不会失败。其次,它有很大的概率将来得到更多人的共识达到引爆点,单个比特币价值百万都极有可能。所以我建议每个月的可支配收入拿出10%买入比特币,我自己目前就是这么做的,虽然现在每个月买的比特币都很少,因为单价已经7万了。

  • 2020-06-04-关于故乡

    刚才在YouTube的越哥说电影上看到了八两金的影评,里面又一次写到了故乡。男主在美国待了16年,然后回到了故乡,遇到了正要离开故乡去美国的女主。就是这样一个故事。越哥说电影里面有好多电影都说到了故乡,以及对故乡的感情。我也想谈谈这个问题。

    不知道是因为我冷血还是什么别的原因,我从来没有感觉到对故乡有什么感情。说的更准确一点,是我对于哪里都没有什么感情。从我出生的湖北黄冈,到我读本科的湖北武汉,读博士的新加坡,工作了几年的上海,到现在临时住的深圳,我觉得哪里都一样,并不存在什么特别的感情。我对哪里都没有什么留恋,有条件的话,我随时就会离开。

    说到更深一点,可能我对这个人世间都有一种疏离感,用句流行的话就是感觉人间不值得。好像罗辑思维的那个老罗说,世间最可怕的是,感觉一切都和自己没关系。我现在就是这种感觉,感觉世间的一切,都和我没有太大关系,我随时可以离开。

    前一段时间也是越哥说电影里面看到一部电影的影评,两个老人在生命的最后一年去完成未了的心愿。我就想列下我自己未了的心愿,想了半天,竟然发现我没有什么心愿。我感觉即使知道明天就会死,我今天还是会这么过。

    也许我该去查查自己有没有得抑郁症了。😂

  • 2020-04-16-批评的自由

    刚才看到李子旸老师的文章,我们需要怎样的批评,刚才看到李子旸老师的文章,我们需要怎样的批评,https://mp.weixin.qq.com/s/2zyaPuOUTxZzZDAIbdqzlg

    下面是我的看法。

    水库论坛宪章里面写着允许造谣诽谤和人肉。

    个人觉得这是最靠谱的。

    从自然法的角度来讲,人天生就长着嘴,他想说什么是他的自由,不管他说的真的假的,或者是不是造谣。

    从经济学的角度来讲,想要一个人不说话实在太难了,你不能把一个人关起来,或者把他嘴封起来。

    李老师所说的批评就是言论自由的一种。愚者千虑,必有一得。1000条批评意见,可能只有一条靠谱,但是为了那一条靠谱的意见能够发表出来,我们必须允许那999条不靠谱的批评意见。如果按照李老师所说的提高批评一些门槛,那一条靠谱的意见可能根本就不会出现。

    至于把那一条意见从1000条意见里面筛选出来的成本,当然是要有被批评者来承担,因为被批评者可以从中受益。

    最后我想说的是,如果没有批评的自由,那么赞美毫无意义。

  • 2020-04-03-老实人吃亏吗

    看了六神磊磊的看病两天人间一叹
    https://mp.weixin.qq.com/s/QIj3nLct0cLUs8XrTOXisQ

    写写我的想法。

    文章里面有两个医生,一个是老实人耐心细致,结果等在后面的患者都纷纷抱怨,另外一个像个黑社会,插队不断,结果反而没有患者敢抱怨。

    作者似乎想表达老实人会吃亏。但是我有不太一样的看法。

    首先,作为一个病人,作者自己是不是老实人呢?显然不是,因为他在后面那个医生那里就顶撞了医生,说他不应该让别人插队,结果那个黑社会医生就不给他看病。反倒是老老实实排队,没有抱怨的那些老实人,不会遭受这种待遇。所以老实人吃亏这句话,至少不是对所有人都成立。当然这些老实人看上去有点怂。

    其次,那个老实医生可能是因为年轻没有太多经验,所以比较细致耐心,唯恐出错,等到他经验丰富之后,可能就会加快速度了。

    最后作为普通人,我们应该怎么做呢?我相信我们大多数人都是老实人,因为痞子毕竟是少数。既然都是老实人,我们就不要去欺负那些老实人,相煎何太急嘛? 另外,作为老实人就要有老实人的本分,老实人最大的长处就是隐忍,没有必要去抱怨外在的环境,多去觉察自己内心的想法。同时谁好谁坏,大家心里都有一杆秤,像作者明显就偏好老实人,相信其他人心里都是这么想的,如果可以选择,大家都愿意和老实人合作。如果是医生,还是要向那个老实医生学习,老老实实的做好自己的工作,别人抱怨两句就让他抱怨去吧,嘴长在别人身上,你也管不着。同时那些抱怨的病人等到自己看病的时候,才会感觉到,这样有耐心慢慢做事的老实人对自己是有好处的,他们就不会再抱怨了。

  • 2020-03-31-承认平庸

    看了九边微信公众号上面的一篇文章,承认平庸可能才是进步的第一步
    https://mp.weixin.qq.com/s/FosZq9P7NzdON-fxgDlVrg

    看完之后,感觉受到1万点暴击。

    把时间当朋友里面有一段话,说现实中的自己和自己印象中的自己是不一样的。这篇文章其实说的也是这个。我们自己总是感觉自己很牛逼,但是现实一次一次告诉我们,我们不牛逼。我们不愿意接受这个现实,这是一些问题的根源。[捂脸]

    甘于平庸和承认平庸还不一样。甘于平庸还有点自欺欺人的味道,意思就是自己本来很牛逼,但是宁愿做个平庸的人。承认平庸就是认清现实,自己真的就只是个平庸的人。

    反省我自己,我从来不愿意承认自己只是个平庸的人,这就是不肯接受现实。不肯接受现实,在我内心里面造成了分裂。我总觉得自己很牛逼,就像王小波说的,走在芸芸众生之中,我感觉我是不同的。但其实这只是一种幻觉。

    突然我有种大彻大悟的感觉。因为如果觉得自己很牛逼,就会找种种证据证明自己很牛逼,过往的全部经历,都可以用来证明自己很牛逼,可以找到一大堆的证据。如果接受现实,承认自己很平庸,就会发现那些证据全都经不起推敲。

    比如我最在意的两个头衔,佛教徒和海归博士。对我来说,最重要的是佛教徒这个标签,但是佛教徒少说也有几亿,对佛学认识比我深刻的,少说也有几百万,真的看不出来到底哪里牛逼了。其次就是海归博士,现在也是满大街都是。

    可能跟我过往经历有关,我小时候我爸妈一直说吃得苦中苦,方为人上人。从来没说吃得苦中苦,做个平凡人。在我的印象里面,甘于平庸都是一种罪过。承认平庸简直是无可救药。

    所以这件事情对我很难。想想我爸妈如果听到我说,我就想做个平庸的人,他们可能会很失望。寒窗苦读20年,最后只是做个平庸的人。

  • 2020-03-22-财富观

    昨天在得到app听到对博多舍费尔通往财富自由之路的解读,我想起来我几年之前看过这本书,而且应该看了很多遍。我打开我的电子书目录,果然找到了那本书,我发现我收集了5个版本,但是现在,关于那本书内容都没什么印象了。

    我在想为什么会发生这种事?只能归结为说里面的内容跟我的价值观不符合,所以书的内容根本没有进到我的心里面去。这本书是讲财富的,说的更直白一点是讲怎么赚钱的,书的副标题叫做怎样7年赚1000万。我重新打开书看了一下,感觉有点看不下去,不知道为什么当时看的津津有味,还看了好几遍。看不下去的原因是里面有一些神神叨叨的东西,比如什么幻想几年之后的生活之类的,豪车豪宅。我心里并不认同豪车豪宅有什么了不起。

    想到这里,我就反思了一下自己的财富观。一直以来我关心的反思的是人生观,价值观,世界观,根本没有想过财富观这个问题。可能因为财富观是价值观的一部分,但是这是一件很奇怪的事情,明明财富观是一个非常重要的话题,为什么我一直都没有仔细或者认真思考过财富观的问题呢?

    虽然不想甩锅,但是我觉得在这个问题上面,我所处的环境或者说整个社会环境,都没有正确的财富观教育,所有人都好像不好意思谈钱,像我爸妈从小也只是说好好学习,没有说怎么好好赚钱,他们所知道的赚钱方法也就是找份工作拿死工资。我们的教育里面更没有教人怎么赚钱。整个社会里面明明每个人都需要钱,但是每个人都不好意思谈钱,即使是朋友之间谈钱都感觉是怪怪的,很少有人会开诚布公的讲自己赚了多少钱,自己想要赚多少钱?

    为什么所有人都觉得谈钱很俗呢?这是很奇怪的事情,钱只是一种工具,就像铲子或者手机一样,没有人觉得一把铲子很俗,或者觉得一个手机很俗,这个俗不是长得很俗那种俗,而是心理上的那种排斥感,没有的人会不好意思去谈手机或者铲子,但是你会不好意思去谈钱。舍费尔在他的书里面提到,正是因为你不好意思谈钱,所以你赚不到钱。

    具体说到我自己,我就感觉自己不好意思谈钱,可能也因为我一直没什么钱,所以不想自曝其短。我也没有一个明确的目标,今年想要赚多少钱,怎么赚到那么多钱。连我设定人生目标,都没有把钱写进去,而是设定的成为优秀软件工程师这样的目标。可能我一直在恐惧,担心自己如果设定一个明确的多少钱的目标会无法实现,担心失败,担心失去自我。想来这还是佛学修为不够的原因,宗萨蒋杨钦,哲仁波切在书里面说,如果你现在没有法拉利,你完全有可能创造出因缘而拥有一辆,只要世界上有法拉利,你就有机会去拥有它。在这个无常的世界里,一切都有可能发生。

    如果佛学的修为够深,不再被执着恐惧和担心失败所控制,那么想要赚到钱不是难事。只不过如果佛学修为到一定的程度,可能对钱也不会那么执着了。

  • 2020-03-08-对于经典的态度

    刚才在得到app里面听了对乱世佳人这本书和对应电影的解读,听完之后,我把电影找来看了一遍,确实经典。

    一直以来,我对经典电影或者小说有一种奇怪的态度,就是想要留到以后某个时间点再看。

    我不知道这种态度是怎么形成的,回想起来可能是因为以前的某个时间点,我执着于看经典就要看原版,但是原版电影或者小说当时看起来太费劲,于是我就想留到未来某个时间点再看,结果那个时间点一直没有来。

    现在得到里面的听书改变了我的态度,通过半个小时的解读,我对这些经典作品有了一个大概的认识,产生了兴趣,然后再找来看就是水到渠成的事情。看完之后,我意识到我以前的态度可能让我错过了很多东西。

    想想我的微信阅读里面有好几百本书,其中不乏经典,是时候啃啃这些经典书了。我觉得对于经典的态度要改变一下了,没有必要把它们放在高高在上的位置,不一定要看原版,甚至不一定要看完。我决定先浏览一下大概,如果感兴趣就看 ,不感兴趣果断放弃,不要认为看不下去就是自己的问题,有可能是翻译的问题,还有可能是与作者的背景相距太远,不能理解作者想要表达的问题和感情,不能产生共鸣。经典的书那么多,换一本就是。

  • 2020-02-21-天文学引起的思考

    在得到里面听了给忙碌者的天体物理学这本书,我产生了一些感想。

    看天文学总是给我一个疑问,甚至会让我对佛学产生怀疑,在佛学看来,整个宇宙是无明的产物。但是这个无明为什么要让这个宇宙如此浩瀚呢?仅仅是为了人类和生命轮回,完全没有必要,把宇宙造的如此浩瀚。

    宇宙存在了100多亿年,范围有几百亿光年,人类才出现了200多万年,活动的范围仅限于地球。

    如果说这个宇宙和人类有什么关系的话,总感觉很勉强。跟宇宙比起来,人类连尘埃都算不上。

    从绝对意义上讲,宇宙并不“存在”。但是从相对意义上讲,宇宙的存在太浪费了。

    相对意义上讲,无明这个万事万物的终极导演为了让人类和所有的生命有一个演出的舞台,搭建了宇宙。我们每个人都在这个舞台上面表演。

    我的疑问是这个导演搭的戏台也太大了点,似乎完全没有必要。按照我的理解,如果我是导演,宇宙只要太阳系那么大就够了,时间也只需要足够人类进化出来就够了。

    当然如果从绝对意义上来讲,所谓的空间大小时间长短都是没有意义的。或许存在其他的宇宙,运行着完全不同的法则,众生在那个宇宙里面进行表演。

  • 2020-02-15-李笑来写作课-摘要

    2020-02-15-李笑来写作课-摘要

    第一节

    改变基础观念,就是两个公式:第一个公式是定义公式,形式是x是什么,不是什么?第二个公式是比较公式,形式是x为什么更好甚至最好?

    写作起码应该是对输入处理过的输出. 例子,苏轼的题西林壁就是经过长期思考沉淀,通过他的文字处理技巧,文字,韵律,意境,最后输出的只有28个字却足够震撼。

    写作一共三个环节,输入,处理,输出。很多人写不出来的原因是输入不够多,处理也几乎没有。

    人们常常把事想反,比如谈恋爱和结婚,再比如考研和出国考试。

    思考能力才是核心关键,如果你能精彩的思考,你就一定能够精彩的写作,当然也能精彩的讲解及,只要你能精彩的处理,你就能精彩的输出。

    绝大多数人没有清楚的表达自己想法的能力。比如王淼同学机票事件。建议,第一去掉没有证据的陈述,第二个,不喊冤不诉苦,第三个只写去哪儿网最不地道的地方。

    千万不要小瞧写作能力,写作其实是在倒逼自己的思考质量,思考能力和所有能力是一样的,都需要通过实践磨练。

    千万不要神化写作能力,写作不是什么太过了不起的能力,只是一个自认为受过教育的人必要必须的能力而已。

    写作能力,除了思考正确之外,剩下的只不过是遣词造句而已,再加上一点点手段,比如倒叙插叙或者用点通感的修辞,偶尔而已。

    一定要把注意力放在自己的思考能力,思考质量,思考习惯上,因为思考质量最终一定会影响生活品质的。

    写作的分若干阶段,第一阶段,锻炼思考能力,第二阶段,锻炼表达能力,第三阶段,说出价值观,第四阶段,积累影响力,第五阶段,你居然可以普度众生。

    你必须自己选择自己的原则,第一,是不是对的,第二,如果是对的,能不能坚守,第三,如果不对,那对的是什么,能不能坚守。

    写作的根本是两条,第一读书,第二多读。

    选书其实比读书更重要。参考选书的基本原则,http://xiaolai.co/books/xiaolai-xuexi/27978d191e5c718c49141ff1cd7351ce.html,此外,朋友圈<好文章<好书<学术文献,免费<付费,通俗杂志<精英杂志,中文<英文,百度<Google<学术搜索,都是提高输入质量的方法。

    根据价值观来选书,第一扔掉不符合价值观的书,第二践行一本符合价值观的书,第三,找一个你认为对的,绝大多数人并不认同的观点,说服一个算一个。

    提高选择标准可以提高提高质量,所以我第一尽量只读非虚构类书籍,第二尽量只读英文书籍。

    无论如何你总是得在意你的输入质量,所以你就得有自己的选择标准,并且还要想办法逐步抬高你的标准,所以再也不要向任何人索要书单了,一定要你自己选。

    一定要反复梳理自己的价值观,就是第一你认为什么是对的,什么是错的,第二,你认为什么更重要,什么最重要。

    不要因为反例动摇自己的价值观,如果动摇,说明不够坚定或者应该修订自己的观念。

    建议每天花半个小时回答几个问题,第一,这事儿对吗?第二,什么更重要?第三,什么最重要?第四,什么事绝对不能做第五,什么事一定要做到的。

    用价值观来判断自己的选择是否正确,所有写作的出发点都是由价值观决定的,什么样的素材值得收藏,值得收藏,什么样的话值得说,什么样的话不适合说,什么样的东西可以接受,什么样的东西坚决不能接受,什么样的理论值得深入研究,什么样的说法不值一提,什么样的人值得深入交往,什么样的人不可不交不可深交,这些都是你的价值观的折射,最终你会发现你的生活开始逐步变得有序的思考,逐步变得有章有法,你的表达开始变得清清楚楚,甚至轻轻松松,这就是树立价值观的重要性,说出了价值观就行。

    没有坚定的价值观,就不可能有坚定的选择。比如我选择做一个长销书作者。

    第二节

    一个人是否聪明取决于两件事情,第一,脑子里是否有足够多的,必要的,清晰的,准确的概念。第二,那些概念之间是否有足够多的,清晰的必要的,准确的连接。

    概念是我们的大脑这个操作系统的核心,所谓的升级我们的操作系统,就是指学习应用新的概念,打磨升级已有的概念。

    每个概念有两个部分构成,第一个是内涵,是那些对象的属性,第二个是外延,指的是概念所指的对象。

    比如分享,必须是自己的东西。

    我会怎么做呢?第一,也许不会做个完整版的笔记会想办法提炼要点,第二,就算我做了完整版的笔记,我也不会把它就那么放出去,第三,若是我做了很多思考,生成了很多补充内容,那我有可能放出去,因为我放出去的是我的东西。第四,若那课程是收费的,我可能也不会把笔记就那么放出去。

    再比如法定节假日,不限制个人,所以不过法定节假日可以比别人多活一半时间。

    我们所使用的语言元素有名词,动词,形容词和副词。名词是用来指称你能感知到某个人或者事物的词汇。动词是用来描述某个人或事物的行为和动作的。形容词性成分是用来描述我们对某个事物的感知的。副词是用来描述我们对某个人或者事物的感知的感知的。

    做几个练习,第一个练习是拿出自己写过的一篇文章,找几根带颜色的笔,把里面的所有名词都圈出来,实物名词用一个颜色抽象名词用另外一个颜色。

    第二个练习是再随便拿几篇别人的文章,把那些名词前后的形容词都圈出来。这是一个极为简单却又常常被忽视的原因,这句话在说明作者不仅对这个能够感知的抽象事物有所感知,还有一个以上的感知。

    我们重新定义一下写作的目标,写作的目标是沟通。第一,你所表达的东西,的确是处理好的吗?第二你处理好的东西你确实表达正确了吗?第三你表达正确的东西,对方真的具备理解的基础吗?第四,对方虽然能理解,他真的会接受吗?第五,对方会表示理解甚至接受,但他真的会当回事儿了,第六,对方有疑问的地方,你准备好进一步解释了吗?第七,对方产生误解的时候,你有对应的方案吗?第八,对方驳斥你的时候,你有足够的心理准备么?

    如果你把写作的目标,讲演的目标,清楚的理解为沟通,那么有效的沟通就是需要做大量的提前的功课的,在多个可能的选项当中选择最有效的那个,这不是很容易的事情,这真的是很难获得的技能,也是很难打磨的技能。

    任何进步最大的障碍,其实是一个看不见的东西叫做没有反馈。

    总结一个沟通的最重要的原则,要想尽一切办法获得所有真实的反馈。

    我给你几条简单的建议,第一,多读心理学方面的原版书籍,第二,多读经济学方面的通俗读物。第三,观察分析拆解揣摩那些公众人物是怎样化解危机的,第四,花时间思考每个真实反馈背后的心理出发点,价值观,给自己一个机会去了解每一类人。

    有效沟通的本质是让对方明白他自己之前不明白的道理,了解他自己之前不了解的事实。

    我们常常需要把叙事顺序也就是叙事结构重新组织一下,以便读者可以从已知开始走到未知,从简单开始理解复杂,从次要起步到达重要。

    所谓的恍然大悟,常常不过是发现原来未知的东西,其实是自己已知的,或者发现复杂的东西,其实是如此简单的。

    修辞的目的是为了让对方感受到他们原本感受不到的东西。类比在沟通中给对方在未知和已知之间搭建了一座桥梁,否则对方走不过去。排比或者重复,哪怕是结构上的重复都会令人产生足够深刻的印象。

    第三节

    写作的一个核心重点是做研究的本领。

    读书可以突破时间和空间上的局限。

    在真实的世界里,就是信息过窄的。

    你要想办法突破局限,你要想办法不给自己设置局限。

    信息过载就是没有可能解决的问题。

    我们去做调查去做研究,其实核心目标就是为了突破局限。

    你必须通过建立完整的知识体系,让自己在一定程度上突破思考质量上的局限。

    所谓的完整的知识体系有三个支点,统计概率,历史方法论和逻辑学。

    一个调查研究过程当中的方法论,就是收集素材的方法,第一个是卡片的应用,正反面有不同的用处,正面摘抄的是例子或者要点,背面是这个例子可能用到的地方。比如猴子的例子。

    好的类比常常是神来之笔,收集类比是学习类比的最重要手段。教育就是眼镜的类比,砖头是科学事实的类比。

    第二个值得收藏的东西是理论。你平时要读书,去收集各种各样的理论。比如,28理论,木桶理论,黄金分割理论。当相信一个谣言的人数比例超过61.8%,其实就是0.618的时候,这个谣言的影响力就可能暴增到100%。

    几个细节需要注意,第一,每一种理论都是有可能被推翻的,第二种,理论都大体上只适用于某些个领域,第三,理论都有一定的局限,并非真理。

    除了理论之外,最重要的是不停的收集思考方式和论证方式。比如复杂二分法,对同一事物用两个维度,两次二分,相互组合就可能出现4种情况。人可以分为聪明愚蠢,勤奋和懒惰。做事分为有用无用,有趣无趣。

    另外一个思考模式叫进一步拆解,进一步拆解也是值得收藏的思考模式。写作是输出->写作是输入,处理,然后输出。理想就是看到了终点。->能够实现的理想至少有三个节点构成,起点,路径和终点。

    读书要给自己时间和机会反过来重新整理,看看好书的作者是如何论证自己观点的,论证过程当中有哪些素材可以用到其他地方,论证过程是什么样的,思考方式是什么样的,自己是否可以模仿一下自己,是否可以拿来创作其他的东西。

    我甚至经常从自己并不喜欢的人身上学到很多宝贵的经验。比如方舟子的文献检索能力。

    文献检索能力就是两条第一搜索搜索再搜索,第二付费付费再付费,第三整理整理再整理。

    只要你收集整理的足够多,各种奇奇怪怪的好运就出现了。

    第四节

    公开写作的意思就是字面的意思,你写好的文章要发出去发表给很多人看,这样的时候除了原本就并不容易做到的有效沟通之外,还有另外一个可能更为核心的要点传播。要传播出去才是真正的公开。

    最厉害的思考方法是思考什么是最核心的因素?

    花越多的时间思考琢磨什么最重要这个问题会让你越来越与众不同,道理也很简单,绝大多数人总是问别人什么最重要,而你是那少数不断问自己这个问题的人。

    公开写作最重要的是传播,没有传播就没有效果,所以呢,你要拼命琢磨写的东西人们读过之后会不会转发。

    无论写成什么样子,最终你要花很长时间去琢磨如何才能让人们把你的文章转发出去,你要满足他们什么样的心理,他们才会洋洋自得的转发,语重心长的转发,意味深长的转发。

    可以做个练习。用一两天时间仔细研究一下你自己的朋友圈,看看你曾经转发过什么文章,如实的记录下来你自己当时的心情,再仔细研究一下你的朋友们转发的文章,多耗费一点时间精力去揣测一下他们转发时的心理,不断把自己想到的东西写在一张纸上,可能是一个词,可能是一句话,暂时不要判断,等第二天再看整张纸上的字句,你一定会有一些心得的,而那些心得就是人们所谓的,只可意会不可言传的东西。

    如何判断文章读者会不会转出去,是文章是否会让读者产生一个明显的变化。只有能够产生变化的才叫影响力。

    正确的传播信息最有效的是人,因为有一些人甚至可能比超级计算机更能有效的处理思想。

    你的作品若是被传播的很广,一定是重要的人起的作用,而不是被机器算法挖掘出来的。

    互联网是个神奇的地方,也是个很奇怪的地方,每个人好像都是公开的,可最终绝大多数人其实是封闭的,你连上互联网并没有太大的意义,真正有意义的是很多人连上你。

    你的传播力取决于你能影响多少个大的节点,你的假想读者群应该是那些有传播能力的大节点。只有他们被触动了,只有他们被感动了,只有他们被震撼了,他们才有可能转发你的内容,只有他们的转发才有以一抵百的威力。

    世界上所有大的节点都是苦心经营的结果,不可能是与生俱来的。

    任何苦心经营出来的东西你都不应该轻视它,若那苦心经营出来的是好东西,那么你一定要重视它,苦心经营出来的是坏东西,你更不应该轻视它,因为它的杀伤力巨大。

    先做一个真正有实力的人,拿出自己的成绩,这才是最核心的关键,更为关键的是无论成长到什么地步,都别忘了自己依然是个有缺点甚至有缺陷的人。

    后搜索引擎时代的策略,首先一定要有一个自己的根据地,可能是微信公众账号,可能是微博,可能是知乎,甚至可能是豆瓣,其次要求助于身边的朋友,若是你开通了微信公众账号,可以告诉你身边的朋友你开了一个微信公众账号,告诉他们想要发表哪方面的文章,你的目的是什么,然后写出你自己真正满意的文章,请他们转发,大不了你拉个群发红包吗?最重要的是学会演讲。

    从一开始你就要深入思考,你究竟要影响什么人,你的长期目标究竟是什么?对你来说什么最重要。你究竟想要什么样的影响力,花多长时间都不过分。

    应对争议要第一应该信奉弱水三千只取一瓢,第二绝不回应,默默拉黑。

    认真研究每个细节是放在哪里都是最重要的技能。微信订阅号的题图,标题,副标题。所以标题很重要。你要花很长时间去考虑标题是否吸引人。题图和logo也很重要,副标题是用来让读者点进来的补充工具,所以一定要想办法在你的文章里放置一些金句。金句都是足够短的,足够精炼的。

    创作的终极技巧是情理之中,意料之外。好的作品,好的文章,好的电影,好的剧集提供了一些你原本想不到的东西,关键并不只在于你原本想不到,关键在于说当你知道了之后,竟然发现那确实是情理之中的。

    找出那些确实足够重要足够本质,读者却没有想到自己该要的,读者获得之后,知道他们确实足够重要足够本质的东西,只有这样你们才有惊喜的感觉啊。

    用户喜欢你是没有用的,得想办法让他们爱你,特别爱你,这才有用。

    制造惊喜的本领是这世界上最有价值的技能,所以一切创作的终极目标都是一样的,制造惊喜。

    传播沉淀与积累,最终靠社区。

  • 2020-02-10-第一本经济学-读书笔记

    第一本经济学

    罗伯特·墨菲

    第1课 像经济学家那样思考 Thinking Like an Economist

    广义上,经济学可以被定义成“关于交换的研究”,包括一切普通市场环境下的交换,也就是卖主交付实物或提供服务,而买主则回报以一定数量的货币。

    经济学研究和解释人们如何进行交换。

    经济学不仅有内在的魅力和在生活中需要的实用性,而且还是当代生活中一个至关重要的主题,因为我们生活的社会正遭受政府的极力干预。

    为了保护我们的社会,基本的经济学知识必须传授给足够多的人。

    这一点与其他自然学科不同,如果街头的张三认为量子力学是个唬人的把戏,那无关紧要,物理学家们不需要张三李四的认同,也照样能进行研究。

    但如果绝大多数人都相信最低工资法能帮助穷人,或者低利率能治愈萧条,那么,训练有素的经济学家们也无力阻止这些政策给社会带来的损害。

    小结

    ·本书指导你像经济学家那样思考。

    不同的学科从不同的视角考察世界,它们有着各自的优势。

    经济学是一个独特的学科领域,它对社会运转的方式有着重要而深刻的见解。

    ·经济学是研究交换的科学。

    在现代经济中,交换通常使用货币,但经济学的原理也同样适用于一切其他类型的交换。

    ·为了防范政府无视经济规律、推行破坏性政策造成的危害,每个公民都应该掌握基础经济学。

    第2课 如何发现经济学原理

    为了给理解经济理论打下坚实的基础,我们必须区分有目的的行动与无意识的行为。

    经济学的法则只适用于前者,而不适用于后者。

    “人拥有自主意识,并试图达到自己的目的”,本书将要推导的经济学原理全都基于这一事实的逻辑蕴涵。

    换句话说,一旦社会科学家们决定承认这一“理论”——人具有独立自主的意识,正如你我亲身体验的自我意识。

    那么就能根据此“理论”自发推导出其他知识。

    小结

    ·有目的的行动是指人为了达到目的而实施的行动,无意识的行为是指在物理世界中,由“纯粹的自然”造成的运动,无意识的行为不涉及人的意图。

    ·自然科学领域的研究对象是自然界,自然科学包括物理学、化学、气象学等,目的是推导“自然法则”。

    而社会科学则包含社会学、心理学、经济学等,研究对象是人类行为的不同方面,包括人们在社会中的互动。

    ·自然科学理论试图更精确地预测无意识对象的行为,它的研究对象遵循一套简单又恒定的规则,并且在多数情况下能进行实验控制,这些因素使得自然科学极其成功。

    而在像经济学这样的社会科学领域中,研究对象具有自主意识,因此极难进行实验控制。

    为了发现经济学原理,经济学家依靠自己体验的有意识行动,并从体验中推导出逻辑蕴涵。

    从这个方面看,经济学不像物理学,更像几何学。

    第3课 由行动推导出的经济学概念

    小结

    ·一旦我们认定某件事是有目的的行动,即可进一步进行逻辑演绎。

    例如,每一个行动都必定有实施者——有意识的人。

    尽管人们能够和别人一起行动,但具体行动只能由个人实施。

    ·我们用人的偏好来解释行动——人通过行动来达到的目的。

    ·经济学家认为偏好是主观的,意思是每个人的偏好都是独特的。

    这并不意味着容忍或赞同那些偏好,而仅仅是认识到人们的喜好各自不同。

    第4课 鲁滨孙·克鲁索经济学

    小结

    ·通过研究仅由1个人组成的假想“经济”,我们能学到许多基础经济学的概念和原理。

    我们在简化的场景里掌握学习方法后,可以应用到更复杂(更真实)的多人场景中。

    ·人所做的最重要的一个决定,是把时间与其他资源投入到现在还是未来。

    通过储蓄与投资,人们牺牲当前的满足,却能在未来获得更多的满足。

    ·经济学家说,只要主观收益超过成本,人将一而再,再而三地忙于一项行动。

    第5课 私有财产权制度

    小结

    ·社会需要制度来制定规则和程序,以避免对稀缺资源的争执,让人们和平地交流互动。

    ·本书将研究三种制度环境——资本主义、社会主义和混合经济。

    ·资本主义制度也被称为市场经济,它的特点是资源的私有制。

    人们可以自由选择职业或开办任何生意。

    不过,他们一定要向财产所有者购买或租赁做生意所必需的资源。

    第6课 直接交换与易物价格

    小结

    ·人们彼此交换,因为期望通过交换获得收益。

    ·只要交换是自愿和诚实的,双方都预期从交换中获益。

    交换导致了双赢的结果。

    ·如果经济学家知悉潜在交易者的偏好排序,就可以描述他们将怎样选定交易条款。

    第7课 间接交换与货币的出现

    小结

    ·尽管直接交换提供双赢交易的机会,但交易者需要找到一个拥有自己想要的东西的人,并且对方也愿意接受自己出让的商品,这就带来了局限。

    这种限制将使人们很难从事专门的职业:想吃肉的牙医必须找到一个牙痛的屠夫。

    ·间接交换扩大了互惠贸易的机会,让更复杂的商品重组成为可能,使得每个参与者获益。

    间接交换最终导致了货币的使用,货币让人们更容易计划交易活动。

    ·货币不是某个人发明的。

    人们通过间接交换来改善交易局面的行动自发地产生了货币——几乎是“意外”的结果。

    第8课 劳动分工与专业化

    小结

    ·货币的优势之一是可以让人们专门从事不同的职业。

    ·劳动分工(专业化),大大提高了劳动生产率。

    当一些人专心种粮食,其他人专心建房子,另一些人则专注于治疗疾病等时,总产出将大增。

    ·即使有的人每一项生产率都拔尖,他仍然可以通过与生产率较低的人交易而受益。

    此时,双方应着重于那些具有相对优势即比较优势的领域,并相互交易彼此的产出。

    第9课 企业家精神与竞争

    小结

    ·企业家是市场经济的驱动力。

    他们雇用工人、购买资源、生产商品和服务并出售给消费者。

    · 竞争促使企业家生产顾客喜爱的物品和服务,并在质量满足顾客要求的基础上尽可能压低要价。

    · 竞争同时也促使企业家按照工人为企业贡献的全额价值付薪水。

    如果支付的薪水过低,那么其他想多赚钱的雇主将以更高的薪水挖走工人。

    第10课 收入、储蓄与投资

    小结

    ·利息的存在,使得个人可以通过储蓄和投资来增加未来的收入。

    今日的勤俭节约,能获得更多未来的消费。

    ·当人们开始储蓄和投资时,将导致经济结构的改变。

    劳动力和其他资源将从电视机和DVD的生产中释放出来,并重新投入工具和设备的生产。

    这将减少当前消费品的生产总量,但新的工具能提高工人未来的生产率。

    ·一个人的储蓄和投资并不导致另一个人深陷债务。

    经济体中的每一个人都能储蓄大笔钱,并在未来享有更高的收入。

    第11课 供给与需求

    小结

    ·经济学家用供给和需求来解释市场价格,以及生产的商品和服务的数量。

    供给与需求并不是“理论”,而仅仅提供一种思维框架,用来理解经济体系中的变化如何影响价格和数量的变动。

    ·供给和需求表(图形或“曲线”)说明了,假设保持其他一切影响因素不变,唯独改变商品的价格所带来的结果。

    重复一遍,供给和需求不是关于经济体系中什么在影响人们的“理论”,而只是经济学家用来组织思维的一个框架。

    ·供给定律是指,如果保持其他影响因素不变,价格上升将导致生产者愿意出售的数量增加,而价格下降会减少生产者愿意出售的数量。

    需求定律是指,如果保持其他影响因素不变,价格上升将导致消费者愿意购买的数量减少,价格下降会增加消费者愿意购买的数量。

    第13课 盈亏计算

    如果消费者的选择是错误的,那么市场高效的满足消费者的愿望只能让消费者在错误的道路上越走越远。

    举个例子,在美国枪支是自由买卖的,所以消费者获得枪支是很容易的,如果消费者想用枪来杀人,那么自由市场其实充当了助纣为虐的角色。

    很多人认为某些盈利的经营项目不道德,但即使在这样的情况下,问题也不是出在追求利润的动机上,消费者对不道德目标的需求才是问题所在。

    举例来说,确实有大量耕地被用于种植烟草,而不是栽培西红柿。

    但最终“强迫”农民种烟草的,不是资本主义制度,而是那么多愿意花钱买香烟却不买沙拉的消费者。

    批评这一不健康结果的人,他们真正反对的,不是私有财产本身,而是吸烟者的自愿选择。

    很多人认为某些盈利的经营项目不道德,但即使在这样的情况下,问题也不是出在追求利润的动机上,消费者对不道德目标的需求才是问题所在。

    举例来说,确实有大量耕地被用于种植烟草,而不是栽培西红柿。

    但最终“强迫”农民种烟草的,不是资本主义制度,而是那么多愿意花钱买香烟却不买沙拉的消费者。

    批评这一不健康结果的人,他们真正反对的,不是私有财产本身,而是吸烟者的自愿选择。

    小结

    ·利息是把储蓄借给或投资于一个项目的正常回报,即投资项目的普遍回报。

    经济利润是企业家从一个项目上赚取的额外回报,即超出同类项目正常利息回报的那部分。

    ·盈利和亏损帮助指导企业家,如何把稀缺的资源用于更好地满足顾客的偏好。

    如果一项经营有利可图,那么这就是一个信号,它表示企业家正在把资源转变成价值更高的商品和服务。

    而如果企业家赔钱,那就是另一个信号,它表示消费者不愿意让资源继续耗费在亏损的经营项目上。

    他们宁愿企业家把资源转投其他项目,以创造出更有价值的商品和服务。

    ·盈亏计算只能反映经营活动的货币层面。

    企业家可以因为个人的爱好继续经营“亏损的”业务,这一决定并没有什么“不经济”。

    但即便如此,盈亏计算反映出别人希望如何使用资源,它让企业家考虑到其他人的偏好,并在了解情况后决定如何使用稀缺资源。

    第14课 股票市场

    小结

    ·股票市场(既包括实体的股票交易所,也包括电脑网络)让买卖双方在一起交易代表公司所有权的股票。

    股票市场决定谁是公司的实际所有者,而这些所有者最终为公司的经营负责。

    ·销售债券是公司筹资的方式之一。

    无论公司经营成功与否,都应该按合同规定还本付息。

    出售股份是公司筹资的另一种方式。

    购买股票的投资者有权按比例拥有公司的收入,不管成败如何,投资者与公司的命运紧密联系在一起。

    ·股票投机者试图“低买高卖”(或“高抛低吸”)。

    成功的投机者修复“定价错误”的股票,因为他们的行动抬高被低估、压低被高估的股票价格。

    第15课 计划经济——理论篇

    要想把握计算问题的本质,我们需要花一些时间来回顾现代经济不可思议的复杂性。

    在制定宏伟的经济计划的第一天,计划者领袖将有一大批可支配的资源,包括:成千上万技能和体力各不相同的工人;石油、煤炭、钻石,以及其他矿产资源;各种工厂、仓库、研究室和教育中心;数以亿计的拖拉机、工具,以及其他损耗程度各不相同的设备;各种基础设施,包括输电线、电话、数据线、公路和桥梁等等。

    别忘了,为了制定连贯的经济计划,他们需要的可不仅是一张要素投入的表格。

    他们还需要知道单位要素的位置。

    例如,如果计划星期二上午安排一名机修工,让他去给一辆磨坏轮胎的拖拉机挂车换胎,那么星期二之前,机修工、新轮胎和拖拉机挂车必须位于同一个城市!然而,即使计划者能用某种方法处理所有这些信息,并根据不断变化的情况,实时快速地更新经济计划,他们仍将无法克服计算问题。

    计划者在专家的指导下能够确定的是,用自己掌控的一切资源可以生产出哪些不同的物品组合。

    尽管他们一心只为人民谋福利,但究竟该如何决定生产哪种组合?

    小结

    ·纯粹计划经济的设想是由计划者拥有全部资源,并通过统一的中央经济计划指挥全部工人。

    他们认为这一制度将比无组织、“无政府”的市场经济更加有效和公平。

    ·计划经济被激励问题困扰,因为当个人的回报不与自己的表现挂钩时,很多人都不太可能会努力工作。

    如果施行“各尽所能,按需分配”的原则,社会总产出很可能急剧萎缩。

    ·计划经济也被计算问题折磨。

    因为生产计划中缺乏了各种资源和劳动力的市场价格,计划经济的计划者将无从得知资源是否得到有效利用。

    也许把资源转用于其他项目,能生产出国民更喜欢的商品和服务,但计划者无法获得足够的反馈来指导决策。

    第16课 计划经济——历史篇

    小结

    ·历史证据不能证实或证伪经济学法则。

    尽管我们通过完美的推理,已经发展出一套精确的经济学法则和原理,但在实践中,若算上推理过程中忽略的其他因素,经济学法则的影响力可能微乎其微。

    因此,观察真实世界的案例是有益的,它可以补充我们对理论的评价。

    ·比较一些起点相当类似的地区——如东柏林和西柏林、朝鲜和韩国——当其中一边实行纯粹的计划经济之后,两边的生活水平差距急剧拉大。

    第17课 价格管制

    小结

    ·干预主义是计划经济和市场经济的结合,它允许个人保留多数资源的所有权,但由政府来管制人们怎样使用“自己的”财产。

    ·最高限价导致短缺、长期供给下降、非价格配给和质量下降。

    最低限价导致过剩、长期需求下降,非价格竞争以及买方提供的非货币条件恶化。

    ·供给—需求曲线有效地说明了最高限价和最高限价导致的短缺和过剩。

    第18课 销售税与所得税

    小结

    ·无论如何筹钱,政府开支一定会从私营部门决定的项目中抽走物质资源,并投入到政治程序选择的项目中。

    ·政府为开支筹钱的典型方式是征税、借债和通货膨胀。

    ·比起自由市场的结果,一切税收都会扭曲经济。

    在非统一销售税率下,将有利于一些商品而不利于另一些商品。

    即使实施统一销售税,也会降低工作的报酬,人为地鼓励人们更多地休闲。

    所得税更是对工作的直接惩罚,它人为地鼓励人们选择具有非货币优势的职位。

    第19课 关税与配额

    小结

    ·重商主义是一种经济思想,它认为积累货币是一个国家通往繁荣的途径。

    重商主义为了将货币留在国内,极力鼓励出口并限制进口,来为国内产业提供就业。

    ·国际自由贸易的论证仅仅是自由市场一般原理的应用。

    只有更多选择才能给人们带来更多收益。

    自由贸易并不强迫人们购买进口商品,它仅仅消除了进口的障碍。

    人人从事专门的职业,并相互交换剩余产品,这再合理不过。

    同样的道理也适用于世界不同的地区,它们应该分工从事专门的生产活动,并彼此交换多余的产出。

    ·关税与配额是政府对外国进口商品采取的人为限制措施。

    与支持者的宣称相反,以人均来看,关税和配额会让一个国家的国民变穷(关税或配额很可能会让一个国家中的某些人受益,但这些人的得益小于其他人遭受的损害)。

    从长远来看,关税和配额不能“创造就业机会”,他们只是改变了就业结构,把工人从效率高的行业转移到效率低的行业。

    第20课 关于禁毒的经济学分析

    小结

    ·不道德的事和违法的事之间有重要的区别。

    就非法毒品的问题而言,从个人道德立场上谴责吸毒,与从法律上支持毒品合法化,这两个立场是前后一致的(如果有人认为出轨者不应该坐牢,并不意味着此人纵容通奸)。

    ·禁毒提高了毒品的市场价格,给贩毒行业带来巨大的货币利润。

    因为非法贩毒如此有利可图,毒品又是“无受害人犯罪”,禁令导致了警察的腐败。

    ·禁毒提高了毒贩的边际收益,降低了毒贩对竞争对手暴力相向的边际成本。

    此外,禁令激励制毒者和吸毒者转向“更刺激”的毒品,从而导致更多的吸毒过量和其他健康问题。

    第21课 通货膨胀

    一直以来,通货膨胀一词人们耳熟能详,不过大家对这个词的意思看法不一。

    历史上,通货膨胀是指经济体中货币数量的增加。

    [插图]但在20世纪的进程中,这个词的意思却渐渐变了,演变成指经济体中商品和服务价格的普遍上涨。

    为了避免混淆,本章我们分别用货币通胀和价格通胀两个专有名词。

    小结

    ·货币通胀是指货币数量的扩张,在美国经济里特指美元总数量的增加。

    价格通胀是指用单位货币计量,商品和服务的价格普遍上涨。

    ·干预会导致系统性的通货膨胀。

    世界主要国家的政府用各种手段强迫人们摆脱基于市场的商品货币(如黄金和白银),并用一纸法定货币取而代之。

    从地下挖掘黄金白银是很困难的,相比之下扩大法定货币的数量却是极其容易的。

    ·大范围地持续价格上涨能够摧毁一个经济体。

    当货币的购买力迅速侵蚀并无法捉摸时,就限制了使用货币的益处,并迫使社会退回易物交换的境地。

    缺少了健全的货币,人们就没有多少储蓄的动机,也不愿作出长期的投资决策。

    第22课 政府债务

    小结

    ·政府的预算赤字是指在一定时期内(如2010年全年),政府开支超出税收的金额。

    政府总债务是在一个时间点上(如2010年5月14日)政府的欠债总金额。

    债务是之前历届赤字和盈余累积的结果。

    ·政府赤字本身并不创造新的货币,也不直接导致价格上涨。

    然而,通过一个非常微妙的过程,政府赤字让美联储购买更多国债,这种做法带来通胀。

    ·政府赤字让后代人受穷,但其原因却不像许多人相信的那么简单。

    如果政府现在借款500亿美元来建造坦克,这些资源(钢铁、电脑芯片、劳动时间等)是由当代人提供的,不是通过时间机器“由我们的孙子买单的”。

    然而,政府赤字真正地把资源抽离私营部门的投资,使得后代人继承的实物资产变少。

    从这个角度看,今天的赤字将使子孙后代变穷。

    第23课 经济周期

    小结

    ·经济周期是市场经济的一种周期性的规律。

    先有几年“繁荣”,其特点是低失业率、高工资、高企业盈利和许多企业扩大经营。

    繁荣之后,紧接着“萧条”和衰退,其标志为高失业率、工资和企业盈利停滞甚至下降、许多企业破产清算。

    ·政府利用中央银行的运作干扰市场利率,导致繁荣与萧条交替的周期。

    通过创造新资金并注入信贷市场,中央银行人为地降低利率,并向企业家发出虚假的信号,让他们扩大经营并投资于长期项目。

    繁荣时期,人人觉得自己很富裕,但这样的繁荣景象其实是假象,因为那不是以真实储蓄为基础的,而是依靠通胀。

    无可避免的崩溃实际上是为回归基本现实而进行的必要的市场调整。

    ·不可持续的繁荣是由信贷市场的通胀引发的,许多工人和其他资源被引导到错误的产业。

    当经济衰退开始后,市场必须重新把它们分配到正确的地方。

    工人的缓慢流动需要时间,这一点表现在高失业率上。

    政府的“救助”措施——比如向失业者发放补贴等——将延长高失业的时期。

    点评

    ★★★★★

    好像是张是之的公众号里面推荐的这本书。

    感觉非常不错,可以作为奥派经济学的入门读物。

    微信读书

  • 2020-01-14-幸好这一切都不是真的

    可能因为这两天一直在看介绍电影的视频,早上我做了一个梦,具体情节我已经想不起来了。

    我记得最后我面临了一个极大的难题,几乎无法解决,但是不知道怎么的,我好像意识到我是在梦里面。

    于是我突然产生了一个想法,幸好这一切都不是真的。

    然后我就从梦里面醒来了。

    梦醒了那个问题当然也就不存在了,醒来洗脸的时候,我突然意识到,其实从佛教的观点来看,我们的人生不也是一场梦吗,我们在轮回的这个梦里面醉生梦死,面对着种种难题。

    如果能像梦里面那样,意识到自己在做梦,那我们所有的难题都已经不再是难题了。

    从理智上讲,我已经能够理解并且相信,自己是在轮回的这个梦里面。

    从行动上讲,目前我还做不到时时观照这一点,我会在梦里面陷入太深,甚至被梦里面的情况和困难,吓出一身冷汗,同时我会被梦里面的欢愉所刺激所吸引。

    也许我需要更多的修行,来做到时时刻刻都意识到那一点:

    幸好一切都不是真的。

  • 2019-12-18-哲学家们都干了些什么-笔记

    哲学家们都干了些什么

    林欣浩

    前言

    “中华民族是世界上最智慧的民族”这句话,我们没听过一千遍,也有一百遍了。但是在生活中,我们会遇到这样的情况:当小孩子难以抑制内心的迷茫和惶恐,急匆匆问爸爸妈妈“人为什么活着”的时候,他得到的回答常常是:“别胡思乱想。”当大学生在宿舍里如饥似渴地阅读康德、黑格尔,想从中寻觅一丝真理的时候,他或许会被打球回来的同学们迎面嘲笑:“又装×呢?”当酒桌上的朋友们都在谈汽车房子情人孩子今年我去了巴厘岛的时候,某人却兴致勃勃地大谈他最近读叔本华的心得,他换来的,多半是满桌审视异类的目光。咦,我们不是号称“世界上最智慧的民族”吗?为什么到处都回荡着对思考者的排斥和嘲笑呢?当我们不断追问“人为什么活着?人生的意义是什么?宇宙的本质是什么?”的时候,绝大多数人不会觉得我们是爱思考的聪明人,只会觉得“你这个人好怪”。

    学哲学有什么好处呢?有时我们会问:“人为什么活着?人生的意义是什么?人终有一死,我该做点什么才对得起这唯一的一生,才算没有白活过?”这些问题很重要,是决定人的一生该怎么活的大问题。我可不可以不听从长辈和课本的灌输,自己来寻找问题的答案呢?可以,哲学就是来干这件事的。我们这本书,就要把“追问人生意义”当作最大的目标。

    第三章 使徒行传

    犹太教和基督教并不是同一个宗教。首先是在犹太人中产生了犹太教,而基督教是从犹太教中发展来的。犹太教和基督教都信奉上帝,也都相信会有救世主来拯救他们(“基督”和“弥赛亚”是一个词,都是“救世主”的意思)。区别是,基督教认为救世主就是耶稣,而犹太教不承认耶稣是救世主,他们认为救世主还没有到来。在对待经文上,两者都信奉《旧约》[插图],但只有基督教相信《新约》。《旧约》和《新约》的区别大致在于,一个是记录耶稣降生之前的事,一个是记录之后的事。

    这是不是说明了他们信奉的上帝不靠谱。

    历史上有一个规律,在斗争中,哲学总站在弱者的一方。这是因为哲学讲思辨,讲道理,而只有弱者才会去讲理。强者不需要讲理。这也是因为,哲学继承了苏格拉底讨人厌的疑问精神。只有弱者在面对强权的时候,才有质疑权威的需要。

    不要说这么直白嘛

    《圣经》说上帝是全知、全能和全善的,那为什么会允许人间存在这么多丑恶和痛苦?我们知道,《圣经》里说亚当和夏娃偷吃了禁果,违反了上帝的禁令,所以被逐出伊甸园,所以人类才会开始无尽地受苦。但上帝是全知的,不仅知道过去所有已经发生的事情,还知道未来所有即将发生的事情。那么前面那个问题就可以问成:上帝既然知道亚当和夏娃会偷吃禁果,为什么一开始不去阻止他们?奥古斯丁的解释是,关键在于自由。上帝给了亚当夏娃和人类自由意志,所以也必须让人类有作恶的可能。更具体地说,上帝是善的,而上帝的善表现在上帝对人类的行为要进行公正的赏罚。既然要赏罚,前提是人类必须拥有自由意志,必须有能力自己选择行善还是作恶,否则人类就不应该对自己的行为负责。这段论证对我们的意义是:首先,它十分巧妙,把一个看似自相矛盾的说法给解释开了;其次,这解释强调了自由的重要性。上帝允许人类有作恶的自由。这说明什么?这说明在上帝看来,自由比善更重要。可是等一等,上帝不是全善的吗?“上帝允许人类拥有自由”的理论是奥古斯丁出于护教目的提出的,其推论却和教义产生了矛盾。矛盾还不止如此,该理论还可以推论出,上帝不能干涉人的自由意志。因为上帝是万能的,所以有能力预测出人们按照自由意志在未来会作出的各种恶,但是有很多恶上帝都没有阻止。可是,上帝不是全能的吗?因此,奥古斯丁的解释虽然聪明,却不是很受基督教的欢迎。

    奥古斯丁其实没有完全解决这个漏洞,哲学家早就证明了,上帝不可能是全能全知全善的。

    宗教天生拒斥思考。

    所以一定要警惕宗教。

    宗教和哲学本来就不能调和。保留哲学,对教会来说就是养虎为患。现代著作家威尔・杜兰就把亚里士多德的哲学比作希腊人留给基督教的“特洛伊木马”。总有一天,苏格拉底的讨厌精神也会让教父们抓狂的。

    作为一个业余哲学家,看了这段话,我对这些哲学家更有好感了。

    第四章 上帝之城

    有句俗话叫“能用钱解决的问题都不是问题”,其实还可以说一句话:“必须用暴力解决的问题都是解决不了的问题”。当强者对弱者使用暴力的时候,正说明强者没有别的招数可用了,也就说明他离失

    还有一句话是暴力不能解决问题,但是它能摧毁问题。

    第五章 异教徒

    九次十字军东征只有第一次算是攻其不备,取得了胜利,后面的八次全部失败。还有一次最奇特,十字军根本没有去打阿拉伯人,而是把东罗马帝国给抢了。要知道,十字军东征名义上的原因,是东罗马帝国扛不住阿拉伯人了,向西边的基督教兄弟求援。谁想基督教兄弟比异教徒还凶狠,东罗马帝国这个惨啊。

    哈哈哈哈哈哈!

    第六章 神们自己

    阿奎纳提出了五个方法来证明上帝的存在。这五个方法形式相近,我们只举其中一个最简单的,大致概括为:世上万事万物都要有另一个事物作为它的原因。那么必然存在一个最初的原因,这个原因就是上帝。这个思路很精彩,它能够完全靠逻辑推理,而不是靠神学教义来证明上帝的存在。我们之后要介绍的很多哲学学派,也是用类似的形式来证明,上帝是存在的。但是假如您和我一样,并不是虔诚的基督徒,那么这个证明还不能满足我们的需要。这是因为,从追问人生意义、追求个人幸福的角度说,上帝对于我们普通人最重要的意义在于:他是全知、全能、全善的。而且人类的灵魂必须永存不灭。换句话说:第一,上帝必须能够知道我们一生中所有的行为和遭遇。第二,上帝必须有无上的善良,以便能对我们的行为进行公正的评判。最好这评判标准还能事先公开,比如通过《圣经》的教诲。第三,上帝也必须有无上的能力,可以对每个人实行奖惩。第四,必须保证每个人的灵魂不灭,这样未来的奖惩才有意义,不至于让我们陷入虚无的深渊。只有具备了以上条件后,上帝的存在才能为我们提供巨大的安慰,才能够指引我们的行动。然而阿奎纳的证明只是说世上存在一个我们无法感知的巨大力量,却无法证明那股力量就是上帝,以及上帝能够具备以上几点能力。

    感觉并不高明啊。

    阿奎纳的证明本身也有问题。罗素反驳说:那什么是上帝存在的原因呢?如果“万事必有因”,那么上帝的存在还要有自己的原因,上帝如果要依赖于外物存在,那么上帝就不是全能的。假如说上帝不依赖于外物存在,那么“万事必有因”就不成立,那我们就允许有事物不依赖原因存在,那你为什么说这初始因就一定是上帝呢,也可以是其他事物啊。对于上帝的理性证明,罗素还有一个反驳。罗素说,上帝是全知、全能、全善的。那么,上帝要不要符合善恶标准呢?假如上帝要遵守的话,那么上帝就有了自己必须遵守的规则,就不是万能的了。假如上帝可以不遵守善恶的标准,那么上帝就无所谓善恶,也就不是至善的了。当然,神学家可以辩护说:上帝是人类不能理解的。上帝的善和人类概念里的善是完全不同的。作为经院哲学的集大成者,阿奎纳对上帝的证明无法令人满意,刚好说明了用哲学推导神灵这条路终究走不通。实际上,用哲学去证明宗教,本身就有一个致命的漏洞。经院哲学家想得挺好,他们用哲学去证明宗教,为的是让宗教也能符合理性的考验。但是别忘了,怀疑是哲学的核心精神。当神学家们试图证明上帝存在的时候,这不也就意味着上帝有可能不存在吗?按照基督教的教义,基督徒绝不能质疑上帝的存在。那么可以说,当神学家们把哲学引入到神学的一瞬间起,他们就已经开始背离自己的信仰了。

    果然被哲学家搞得一头包。

    第七章 群魔

    中国人对待宗教有更多实用主义的倾向,信宗教大多是为了要点好处。而且佛教说的是因果报应,就算你不信佛,多做好事也可以有好报。不像基督教讲人有原罪,光做好事没用,你不信仰基督不受洗就进不了天堂。中世纪的教会认为,刚出生的婴儿如果没来得及受洗就夭折了,那也是要下地狱的。

    第九章 奇怪的论调

    哲学和科学一样,也有现成的产品呀!那就是充斥在我们生活中的各种各样的人生观。当邻居大妈默念“人的命天注定”的时候,她信奉的是宿命论和决定论;当朋友在酒桌上劝你“赚钱有什么用,钱再多早晚也是一个死”的时候,他讲的是虚无主义;当人生感悟型的散文告诫你“当下最重要,活出你自己”的时候,它其实就是萨特的代言人。实际上,整个哲学史上那么多学派那么多说法,其中凡是和普通人有关系的观点,我们都可以在生活中找到它们的通俗版、谏言版、人生感悟版、心有戚戚焉版。我们不需要了解真正的哲学理论,就已经在“享用”哲学家们的思考成果了,并没有什么精妙的哲理是独独藏在哲学著作里,是我们在日常生活中享受不到的。你想,假如这世上存在一种让人易于接受的,又能给人带来好处的道理,人们没有理由不把这个道理改写得通俗易懂,然后拼命到处传播呀。每个人天生都趋乐避苦。那么假如哲学书中真有什么对人类有好处的人生道理而大众却不知道的话,这不就意味着只有我们才是全世界最精的人,而这世上其他所有不读哲学的人都是比我们笨的傻子吗?这不大可能吧。

    我们享受科学成就最好的办法是买个新手机而不是去学《电子电路》一样,如果我们的目的是为了找一个对自己有好处的人生观,那我们没必要学习哲学,只需要从各种世俗的人生观中选一个就好了。假如明白了这一点,你还是不满意各种世俗的人生观,执意要翻开哲学书亲自研究一番的话,那么就只有一个原因了:你不信那些现成的答案。你怀疑它们。祝贺你,你被苏格拉底附体了。为什么苏格拉底宁愿死,也要怀疑?为什么我们放着现成的快乐不享受,非要亲自学哲学?因为我们是人,不是动物。人和动物的区别在于人要思考。而怀疑是思考的起点,也是思考成果的检验者。怀疑的最大作用在于能避免独断论,这样才能引导我们寻找正确的答案,免得我们轻信一切未经证实的结论。

    第十章 童年的终结

    度高者重表,测深者累矩,孤离者三望,离而又旁求者四望。触类而长之,则虽幽遐诡伏,靡所不入。

    这种形式的勾股定理,想想就觉得恐怖。

    第十一章 理性主义

    我们的结论必须能经得起各种怀疑,这样才能保证它真实可信。这也是科学研究的原则。

    欧氏几何是什么东西呢?它一共有五条公设和五个公理。这些都是欧几里得硬性规定的。然后他的整个几何世界,所有的定理,都是从这几条公设和公理中演绎推理出来的。我觉得,咱们普通人只要一学欧氏几何,肯定都匍匐在地上把它当成神了。您先看看它的五个公理和前四个公设,不用细看,扫一眼就行:公理一:等于同量的量彼此相等。公理二:等量加等量,其和相等。公理三:等量减等量,其差相等。公理四:彼此能重合的物体是全等的。公理五:整体大于部分。公设一:任意一点到另外任意一点可以画直线。公设二:一条有限线段可以继续延长。公设三:以任意点为心及任意的距离可以画圆。公设四:凡直角都彼此相等。感觉到了吗,这些公理和公设都超级简单,全都是小学课堂上一句话就可以带过的知识。大部分在我们看来就跟废话一样,都想不出,写出这些来能有什么用。然而,就是这么区区几句话,竟然能一路推理推理,写出厚厚的前六卷《几何原本》来,内容能够涵盖世间所有的平面几何知识。几何世界千变万化,大自然中的几何图形更是无穷无尽,都逃不过上面这简单的几句话。这能不让人膜拜吗?但这还不是最牛的。咱们来看看剩下的第五公设。内容是:若两条直线都与第三条直线相交,并且在同一边的内角之和小于两个直角,则这两条直线在这一边必定相交。你一看不对劲了吧。这个公设超级复杂,跟前面的公理和公设的简洁形式毫不搭配。更可疑的是,在《几何原本》里,第五公设仅仅在第二十九个命题里用过一次。就好像是一个根本没必要的累赘一样。

    第十二章 形而上学

    “形而上学”这个词英文是metaphysics,它的来历是这样的:话说回到古希腊。亚里士多德是个百科全书式的学者,他写过很多的著作,从哲学到物理学,涉及了很多学科。但是那个时候没有现代学术界“哲学”“物理学”这样的分科。亚里士多德是写痛快了,想研究什么就写什么,可给整理他书籍的后人犯愁了。这么一大堆包罗万象的著作,该怎么分类、命名呢?一个叫安德罗尼柯的人想了一个好办法。他用“研究有形体的事物”和“研究没有形体的事物”,把亚里士多德的著作分成了两大类。前一类著作编在一起,起名叫《物理学》。后一类作品,也就是亚里士多德的哲学作品,也编在一起,放在《物理学》的后面。当时没有合适的名字称呼它们,安德罗尼柯一看怎么办呢,就给起了一个名叫metaphysics[插图],意思是“物理学之后”。安德罗尼柯起这个metaphysics的原本目的,应该是他没有现成的词汇可用,于是就说这部分著作是“编排在《物理学》之后的内容”。但这个词的含义也可以引申成“物理学之后的学问”。也就是说,形而上学研究的是那些高于物理学的、看不见、摸不着的学问。这就是“形而上学”这个词最早的来历。

    “形而上学”的中文译名也很棒,称得上是中文翻译史上最棒的译名之一。中文典出《易经》:“形而上者谓之道,形而下者谓之器。”《易经》的这句话很精彩,也很好理解。“形”,就是有外形、可以触摸、可以感知的东西。《易经》的这句话,是下了两个定义。第一个定义是说,超过我们感知之外的那些无形的东西,是“道”。“道”,就是“道理”的“道”,指的是“道理”“概念”这些抽象的东西。老子说“大道无形”,就是这个意思。第二个定义是说,我们能感知到的那些有形的东西,是“器”。“器”是“器具”,就是指“东西”“物质”。让人拍案叫绝的是,《易经》的这句话,和安德罗尼柯的思路是一模一样的。《易经》的“道”,对应的就是安德罗尼柯的metaphysics。《易经》的“器”,对应的就是安德罗尼柯的“物理学”。于是日本哲学家井上哲次郎先生在看到metaphysics这个词后,联想到《易经》,把metaphysics翻译成了“形而上学”。简直是“信、达、雅”的最高境界。

    很多小孩喜欢不停地问家长“为什么”,让家长不胜其烦。其实,这个“为什么”的游戏玩到最后,追问的往往就是形而上学的问题。举个例子。小孩问爸爸:“我为什么要上幼儿园啊?”爸爸回答:“因为爸爸妈妈要上班,不能照顾你呀。”“那爸爸妈妈为什么要上班啊?”“因为爸爸妈妈要挣钱啊。”“那爸爸妈妈为什么要挣钱啊?”“挣钱了才能买吃的啊。”“那为什么要买吃的啊?”“有了吃的,人才能活啊。”“人为什么要活着啊?”一般问到这种地步,家长就准备打人了,对吧?可是,家长要打人并不是因为孩子无理取闹——求知怎么能算是无理取闹呢?而是因为家长没有能力回答这个问题,他们恼羞成怒了。因为孩子最后问的“人为什么活着”的问题,正是形而上学最重要的问题之一。加缪说过:“真正严肃的哲学问题只有一个,那就是自杀。”研究“人为什么不自杀”,其实就是在研究“人为什么活着”。你看这孩子一下子就提出了一个最根本的哲学问题,一般的家长怎么可能回答得上来呢?

    我儿子有几次就把我问得一头汗,因为我答不上来,他的问题更简单,为什么呢?其实前面一个问题他未必理解了,但是他还会接着问后面一个问题为什么呢。

    第十四章 唯我论

    笛卡尔说过:“不管多么荒谬、多么不可置信的事,无一不是这个或那个哲学家主张过的。”这句话使他不仅成为了伟大的哲学家,还成为了哲学史上伟大的预言家。在笛卡尔之后,我们将会看到更多稀奇古怪的奇思妙想。您会发现,您小时候觉得自己有过的特离奇的想法,这帮哲学家们早就全都给想遍了。

    突然对这本书充满了期待。

    第十七章 名利场

    亚里士多德是柏拉图的学生,但是观点和柏拉图相悖,为此亚里士多德还说了一句名言:“吾爱吾师,吾更爱真理。”你可以把这句话理解成亚里士多德对真理的浓浓爱意。但你也可以理解成:“有理就说理,别拿辈份压我!”

    好吧。

    有个皇族成员想光耀门楣,叫莱布尼茨替他写一部家族历史。莱布尼茨满口答应了(可能还是他主动提出来的)。您想,其实这不就是个软文吗?你找点史据把他们家夸一顿不就完了嘛,随便来个会写字的都能干这活儿。然而莱布尼茨是怎么写的呢?他说,这个家族的历史,是整个皇族历史的一部分,必须和整个皇族的历史结合在一起写。但是要研究皇族的历史呢,又必须先研究地理。然而这片土地又是地球的一部分,所以我们要从地球的形成开始研究。然后那个皇族成员左等右等也不见莱布尼茨的书写完,就派人去看看。结果那人一看就崩溃了:莱布尼茨正兴致勃勃地写远古时代的地球发展史呢。所以说,古板的知识分子就算是想市侩,结局恐怕也是悲哀。

    我笑喷了。

    第二十一章 暴风雨

    什么叫因果律呢?你不能说因果律就是“一件事的发生是另一件事发生的原因”,这相当于同义反复,说了跟没说一样。因果律是什么呢?在经验世界里,我们可以把因果律说成:“如果A事件发生了,那么B事件一定会发生。”更严格的说法是:一、A事件发生在前,B事件发生在后。二、二者发生的关系是必然的。比如说,苹果必然落地的事件我们可以分解为:一、“苹果离开树枝”发生在前,“苹果落地”发生在后。二、这个关系是必然的。想象一下如果我们是一个一无所知的小孩子,我们只靠经验,怎么能知道苹果一定会落地呢?唯一的办法就是,我们一遍又一遍观察到“苹果离开树枝”和“苹果落地”这两件事总紧接在一起发生。我们就明白了,哦,苹果这东西原来不可能飞上天去啊。但问题是,通过经验,我们观察到的只是因果律中的第一条:A事件发生在前,B事件发生在后。那么第二条呢?这个关系的必然性我们是怎么观察到的呢?这个“必然”能让人看见?这个“必然”能让人感觉到?没有,“必然”这个东西不在我们的经验范围之内。我们之所以认为这里有“必然”性,是因为我们过去无数次地看见了这两件事连在一起发生,所以我们就想当然地认为,这两件事之间有必然的联系,在未来也会永远连在一起发生。休谟尖锐地指出:这种想法是错的。

    什么叫做彪悍,这就是了。

    第二十三章 谎言的衰落

    这是在笛卡尔出生半个世纪之前,那时候还没有马丁・路德,还没有新教,连赎罪券都还没有呢,就已经有人看罗马不爽了。这个人是神圣罗马帝国的皇帝亨利四世。他觉得自己很牛,为啥我的国家非要每年给罗马教会捐那么多钱呢?他就开始和罗马吵,吵到后来他竟然宣布罗马教皇是伪僧侣,要其下台。教皇对付这种不服的主儿,只有一个办法:“绝罚”你。虽然教皇翻来覆去只有这么一招,但这招儿太灵了。我们前面说过,欧洲国王管不住自己手下的领主,领主们又信奉教会。亨利四世被“绝罚”后,立刻叛乱四起。亨利实在扛不住了,无奈之下,不得不千里跋涉来到教皇的住所前求饶。贫民出身的教皇拒绝接见他,亨利就在大雪中站了三天三夜(一说还没穿鞋子),然后教皇才出来让他吻了自己的鞋,宽恕了他。这个例子经常被提起,用来证明中世纪教皇的权威。但是有些文章忘了说故事的后半段:亨利四世是个很厚黑的家伙。回去以后一看没什么事了,嘿嘿一笑从怀里掏出一个小本儿来:谁当初背叛过我,我都记着哪。没过多久,亨利四世把当初背叛他的人都给灭了。稳定了局势后,他立刻翻脸再次讨伐教皇。教皇只有一招呀,“绝罚”呗。连圣斗士都知道同样的招数不能用两遍,何况是对政治家。面对第二次“绝罚”,亨利四世啥事也没有,直接带兵杀到了罗马。教皇只能从罗马仓皇出逃,最后凄凄惨惨地客死异乡。当然,这时候教会势力还很强盛,后来继任的教皇又把局势扳回去了。宗教改革的时候,新教还拿这件事出来说,用来激励日耳曼人的民族情绪,号召人们为亨利四世报仇。

    好吧,我一直都只知道这个故事的前半段,没想到还有后半段。一个神反转。前半段说的是教会多牛逼,后半段就变成了教会傻逼了。

    第二十四章 远离尘嚣

    康德是个奇幻作家,给我们设计了一个架空世界。这世界是什么样的呢?在这个世界里,人类是一种非常可怜的生物。人类永远无法认识到这个世界的真面目。人类所感受到的这个世界,都是通过人类心灵中某个特殊的机制加工处理过的。这个负责加工的机制,我们起个名字叫作“先天认识形式”。世界的真面目,起个名字叫“物自体”(也被译作“自在之物”)。人类感觉到的世界,也就是“物自体”经过“先天认识形式”加工后得到的东西,我们把他(们)叫作“表象”。这几个名词,得麻烦大伙记一下了。也就是说,我们生活中看到的桌子啊,椅子啊,这些都是世界的表象。桌子和椅子的真面目是物自体,到底是什么样子的我们永远无法知道。要特别说明的是,这个先天认识形式,也就是人类心灵对物自体的处理机制,每一个人都是一样的。这个“先天认识形式”一词中的所谓的“先天”,不是说这东西是生物学上的天生的本能,不是像理性主义者说的那样是一种超越了客观世界的存在,它既不是人类生理的表现,也不是心理的表现,不会因为人体的变化而改变。所以你说我想改改自己的“先天认识形式”,这是不可能的。顺便我们再学一个小词汇:“先验”。“先验”和“先天”差不多。意思是,先于经验,说某些东西是在人获得经验之前就存在的。这些东西不依赖于人的经验而存在,而且常常会决定着人的经验。显然,先天认识形式就是先验的。再比如理性主义者相信的不言自明的公设,一般人理解的绝对真理,也都是先验的。

    康德认为,这世界(物自体)是人类永远无法真正认识的,人类看到的只是表象的世界。但是由于每个人对真实世界的表象方式(先天认识形式)都是相同的,所以人类看到的同一个东西的感受还是一样的,因此我们察觉不到真实的事物是否被扭曲了。所以这个世界观并不和我们的生活经验相悖。那因果律是怎么回事呢?康德认为,我们这个先天认识形式里,包含了很多用来处理物自体的工具(一共有十二个先天范畴),其中一个就是因果律。而科学家们只能研究我们感觉到的事物。也就是说,科学家只能研究表象世界,因此科学家的研究对象都是带有因果律的。那么,人的自由意志又在哪儿呢?我们自己的意识就是物自体啊!因果律只存在于先天认识形式里,并不存在于物自体中。物自体是自由的,我们自己的意识也是自由的。换句话说,康德让人的意志受到了先天认识形式的严密保护,因果律不能穿透先天认识形式去控制人的内心意志,所以人仍旧是自由的。当然,这也意味着作为物自体的自我意识,是没法被我们察觉和把握的。也就是说,科学是永远无法研究人的自由意志的。问题完美解决。

    不知道为什么我觉得跟佛教的理论很类似,佛教里面讲真俗 二谛,所谓的真谛就是我们无法描述无法理解超越理性的绝对真理,而俗谛就是我们轮回中的相对真理。

    比如空间和时间的概念,就是人在学习一切知识前,必须先具备的先天认识形式。康德给出了几种证明方法,我们说两个简单的:第一个证明是,我们有感觉对吧,而“感觉”暗含的意思是,我们感觉到的是“我们之外”的东西。不用人教,就知道自己有意识,自己的意识之外还有一个世界。这“之外”两个字,就说明我们有空间概念。换句话说,如果我们没有空间概念,我们的感受就是一片混沌,连什么感觉是属于自己的、什么感觉属于外界的都不知道。自然,在这种状态下,我们也不可能再去学习空间的概念。所以空间这个概念是先于经验的,而且是每个人必有的。第二个证明是,人类可以想象不存在物体的空间,但是不能想象不在空间中的物体。这说明空间是不依赖外界经验存在的概念。同样的道理,时间概念也是先验的。

    佛教里面讲涅盘也是超越时间空间看概念的。

    假设出了一个交通事故,有一个警察去调查,调查回来说:这个事故不是在任何时间发生的,也不是在任何地点发生的,也没有任何发生的理由。那么警察局局长一听肯定气疯了。哪怕这警察胡编一个时间、地点和理由,警察局局长也不会那么生气。为什么呢?假如警察胡编了时间、地点和理由,好歹我们有机会知道他说的是真话还是假话。但他这个没有时间、地点和理由的报告呢,对我们来说是完全不能理解的。我们根本就没法理解这么一句话。这说明一个知识如果不具备时间、空间和因果律的要素,我们就完全不能理解。也就是说,只要我们有关于某物的知识,这知识必定伴随着时间、空间和因果律等等。时间、空间和因果律的概念是先于我们的经验而具备于我们思维中的。

    佛教里面说,我们甚至无法想象不在时间和空间之内的事物。所以涅磐这种超越时空的境界只能通过修行亲自证得,不能靠理智来理解,也没有办法传授,因为我们无法用语言描述,因此我们只能用相对的方法来逼近这个真理。

    康德的解决方法是,他把世界分成了两个部分。一个部分完全不可知,另一个部分则可以用理性把握。不可知的那部分因为永远不可知,所以对我们的生活没有什么影响。只要我们在可把握的世界里生活,理性就又恢复了威力。这样,既没有破坏休谟的理论(想破坏也没那能力),又让人类重新信任理性,重新踏实了。康德的学说并不是和我们完全无关的玄学,而是有很重要的现实意义。假如我们接受康德的世界观,我们就同意,这世上总有一些东西是我们无法认识的。我们只要安于在能认识的世界里生活就对了。

    确实很有用处。我又联想到了佛教。

    康德的哲学工作并不仅仅停留在构建一个新世界观上。康德还有一套伦理观,他觉得也很重要。就像康德的名言:“有两种东西,我对它们的思考越是深沉和持久,它们在我心灵中唤起的惊奇和敬畏就会日新月异,不断增长。这就是我头上的星空和心中的道德定律。”然而我觉得,这部分伦理观对于今天我们这些普通人的意义并不大,我只说一个有趣的地方。康德的伦理学是建立在他的形而上学基础上的。康德认为,人的道德也是存在于先天认识形式中的,因此所有人都要受到道德律的支配,这是无条件的。这样,康德给每个人都必须遵守相同的道德找到了哲学上的根据。同时,因为康德这里的道德是先验的,因此它必须发自内心且不受外界的影响。所以,我们说,如果一个人做好事是为了得到表扬,在康德这里就不算道德。

    康德那段话很有名。

    康德身体不太好,有几年,他每个月都要向当地警察局询问死亡统计数字,以便估算自己的寿命。但是康德又不信任医生,就自己规定了很多古怪的守则,而且严格遵守。虽然这些守则有些非常怪,但事实证明康德是很成功的,在那个医学不发达的年代,他活到了八十多岁。都有什么怪规矩呢?——康德觉得吃药多了对身体不好,他就自己规定,无论医生怎么说,一天最多只吃两片药。为了避免伤风,他还规定在除了夏季之外的季节里,自己在散步的时候不和任何人说话。他规定自己每天只抽一烟斗的烟,但是据说他的烟斗一年比一年大。他讨厌出汗,一旦发现自己要出汗,就静静地站在阴影里,好像在那等人似的,直站到要出汗的感觉消失。他还在一本小册子中介绍自己在睡觉时对抗鼻塞的招数:“紧闭双唇,迫使自己用鼻子呼吸。起初很吃力,但我不中止、不让步。后来鼻子完全通了。呼吸自由了,我也就很快睡着了。”对抗咳嗽呢,“方法如下:尽最大的力量将注意力转移一下,从而阻止气体喷出”。其实用一句话就可以概括:有症状就硬憋着。这都是什么治病方法呀!

    突然觉得这老头很可爱。

    第二十五章 王者之风

    形而上学家们认为自己研究的是世间万物的本质,黑格尔也是如此。他认为他的形而上学并不是在书斋中的空谈,而是能解释世上所有事物的发展规律的。他说一切事物都在绝对精神的统御下,朝着进步的方向发展,包括整个人类的历史也是这样。因此,黑格尔说:“凡是合乎理性的东西都是现实的,凡是现实的东西都是合乎理性的。”按照我的理解,这里的“理性”指的是绝对精神。“现实”指的是符合历史必然性的事物。这句话的意思是,所有合乎绝对精神的事物,必然会发生。说白了,意思是历史一定会按照绝对精神的要求前进,不会例外。

    划重点,期末考试要考的。

    黑格尔说世间万物的发展一定要符合绝对精神,因此他的绝对精神观是决定论的,他认为历史不是人类创造的,也不是个别事件的堆砌,历史有自己必然的进程,我们人类只是历史实现自己目的的工具。就算拿破仑那样的伟人,其实也是绝对精神的工具。并不是拿破仑自己要征服欧洲,而是绝对精神要利用拿破仑来推进历史的进程。所以黑格尔表面上赞扬的是拿破仑,其实是在赞扬绝对精神。

    黑格尔的历史通向哪里呢?黑格尔的历史通向绝对精神。他认为宗教比自然科学更高级,哲学又比宗教高级。最后,绝对精神会通过哲学完成自己的发展,达到最完美的境界。具体说来,那个被绝对精神决定了的、借以认识世界、实现绝对精神的人是谁呢?黑格尔说:就是我!就是爷本人!黑格尔认为自己揭示了整个世界的规律,他已经说明了这个世界的本来面目,完成了绝对精神自我实现的任务。黑格尔的哲学就是一切哲学的终结,哲学发展到他这里就到头了。

    不知道黑格尔是不是哲学家里面最会吹牛逼的一个。

    黑格尔认为世界一切事物的发展都要符合辩证法,这个看法太教条了。但有时辩证性的确有道理。比如,一个人是怎么成长的呢?一个人先有一个原有的思想(正题),然后在生活中遇到了这思想不能解决的问题(反题),思想和现实问题发生了冲突,才会引起他反思人生。这个反思的结果不可能说最后完全不顾以前的旧想法(正题),最后的新思想(合题)肯定是结合了正题和反题。这就代表着人变得更成熟了。辩证法还说,所有的正题都有反题,这提醒我们要把事物和它的对立面放在一起综合来考虑。用术语来说,当我们看到一个现象的时候,光孤立地看这个现象,那样的层次会比较低。假如我们找到这个现象的反题,再把正题和反题合在一起,分析正题和反题之间对立与统一的关系,从而观察到它们的合题,那我们看事物的能力就能提高一个级别了。

    您应该会同意,我们追求个人幸福的最高境界并不是有钱有权有一大堆情人围着,并不是肉体享乐。哲学史上也没有哪个哲学家认为纵欲是快乐之道。连古希腊的享乐主义者追求的也不是肉欲的极限,而是适度的享乐、劳逸结合的生活。这是因为大家都发现一个问题,肉欲快乐固然很好,但是纵欲总是和它的反题——痛苦、空虚紧紧连在一起的。不存在某种只给人快乐、不带来痛苦的享乐。这正符合了辩证法的观点。所以最后的结论就是,我们追求个人幸福的最高境界,不是纵欲,而是内心的平静。

    涅盘寂静。

    第二章 悲观主义

    休谟说我们的理性根本无法认识这个世界的真相。康德点点头:休谟说得没有错,我们的确无法认识物自体,可是我们生活在表象的世界里。在这个表象世界里,一切都先经过先天认识形式的加工。而先天认识形式我又用理性给分析得清清楚楚了,那表象世界还是被理性统治的呀[插图]。所以在康德看来,理性就是我们这个世界的统治者。没错,理性确实管不了物自体,但是物自体也不影响我们的世界呀。叔本华说,不,物自体能影响我们的世界。不仅能影响,而且影响力超大,我们用理智控制不了。在康德那里,这个世界的基础是井井有条的理性。在叔本华这里,这个世界的基础是无法控制的生命意志。因此康德对世界的看法是乐观的。叔本华对世界的看法是悲观的。

    叔本华说,全宇宙的生命意志只有一个,这让人想到了斯宾诺莎的实体论。斯宾诺莎说,世界上所有的事物都属于同一个实体。但是斯宾诺莎的理论里,实体是完满至善的,所以我们每个人都因此得到了幸福。而叔本华说,生命意志是邪恶的,是痛苦的源泉。所以每个人都逃脱不了痛苦。叔本华为什么这么说呢?满足欲望会带来快乐,这没错。但是叔本华认为,欲望本质上是痛苦之源。因为满足不了欲望,人会痛苦。满足了欲望,人又会产生新的、更高的欲望,还是会痛苦。叔本华打比方说,满足欲望,就好比施舍给乞丐一个硬币,维持他活过今天,以便把他的痛苦延续到明天。叔本华还引用一句法国谚语,说明人们无止境的欲望:“更好是好的敌人。”如果人满足了全部的欲望,而且没产生新的欲望,人会幸福吗?不会,人会感到空虚和无聊,这也是痛苦。所以快乐只是暂时的,痛苦才是永恒的。人生就好像在痛苦和无聊之间不停摆动的钟摆。这种情景就像王尔德说的:“人生有两大悲剧:一个是得不到想要的东西,另一个是得到了。”而且前面还说过,生命意志还是盲目的、永不疲倦的。可想而知,人类被这么一个没法打败又只能带来痛苦的东西控制,那人生自然是一个悲剧。我们记得,康德费尽千辛万苦,才给人类找回了自由意志。而在叔本华这里,自由意志又没了。

    在佛教里面叔本华的生命意志有另外一个名字,叫做轮回。不过在佛教里面,轮回并不是最本质的,超脱轮回之后的境界才是最本质的,它的名字叫做涅磐。

    叔本华认为,基督教教义中的原罪就是生命意志,基督教鼓励的赎罪精神就是要求人们去克服生命意志。所以基督教比其他宗教在欧洲更受欢迎,因为它认识到了生命意志的悲观主义精神,而其他宗教都是乐观主义的。佛教比基督教更重视禁欲。基督教在某种程度上并不禁欲,比如鼓励人多生养,鼓励人勤奋工作。佛教不同,佛教认为欲望是痛苦的来源,主张彻底摒弃一切欲望。这和叔本华的观点很像。这不是巧合,叔本华的哲学观点深受印度佛教的影响。据说他的书桌上经常摆放的是一尊康德像和一尊佛像。

    果然如此。

    悲观主义对于我们来说仍旧有现实意义。首先,叔本华的悲观主义从某些角度上看确实是成立的。虽然说理性未必就会败给欲望,但对于大部分人来说,欲望的确是生活的主题。我们是为了获得尽可能多的安全感,为了有更好的物质享乐,为了和别人攀比,才会去忍受无穷无尽的艰辛劳动和在各种挫折中的垂头丧气。大部分人这一辈子活着,为的都是满足各种各样的欲望。我们也同意,欲望是永远不会被满足的。满足了就会产生新的欲望,不满足就会产生饥渴感。所以叔本华的世界观对于大部分人来说,是对的、没问题的。叔本华提出的解决方案也没问题:既然满足欲望是一条不归路,那我们就应该早点看清这一点,不再去满足欲望。但欲望不满足我们会饥渴痛苦啊,那就可以像叔本华建议的那样,用无关欲望的对艺术品的欣赏来获得暂时解脱。这也是被社会普遍接受的生活观。人发财了,整天酒池肉林,追求物质享乐,并不是个长久的选择。不管多好的享乐,玩一阵子就会觉得没劲了,感到空虚无聊。一些有钱人想不明白满足欲望是条不归路,还在不断追求更强烈的刺激,满足更大的欲望。可是享乐的标准不断水涨船高,每一次获得相同快乐所需要的金钱越来越多,最后总有一天会捉襟见肘,把自己玩穷。同时,人的精神和肉体所能承受的刺激也是有限的。不断地追求更多的刺激,最终只能是找死——赌钱、飙车、冒险、吸毒。但是欲望还是不会停啊,再往前走,就只有自我毁灭一条路了。另一种有钱人,当他们玩一玩、发现纵欲也不过如此以后,他们的业余生活就不再追求物欲,而是改成追求艺术。这是古往今来很多有钱人的选择。

    在佛教看来悲观主义过于强调苦的一面,所以毕竟还差了一层,在佛教看来,苦是可以解脱的,四圣谛里面的道谛就是解脱的道,我们每个人的本性里面就包含着解脱的道,我们每个人都有可能达到涅盘寂静的解脱境界,相比于悲观主义,佛教更有希望。

    悲观主义能给我们带来很大的安慰。悲观主义让我们把整个世界都看成是一个很差的地方。那么,我们就不必对这世界期待太多。当这世界损害我们的时候,我们也不会感到不公,或者觉得很失望。同时,我们对这世界的期待少了,我们自己的生活压力也就小了。因为人生再怎么折腾也是悲观的,那我们何必要一味去奋斗?比如,如果相信叔本华的理论,你会觉得,无论挣再多的钱、获取再高的社会地位,得到的仍旧是不能满足的欲望和空虚。这不比混吃等死好到哪去,反而还会因为追名逐利而放纵了自己的欲望,让自己更加痛苦。这么一想,也就没有什么生活压力了。悲观主义的另一个好处是,他能让你意识到世界上的其他人和你一样注定痛苦,无论那人多么有钱多么风光也是一样。那么相比之下,自己的痛苦也就会好受一点。嫉妒和憎恨是一般人难以摆脱的痛苦之源,当你意识到你所嫉妒或者憎恨的人也注定摆脱不了悲观世界的时候,心里也会好受多了。

    第四章 瞧!这个人

    叔本华,一般人以为他是一个悲天悯人的慈祥老头。不!生活中他暴躁刻薄。尼采,一般人以为他是一个放荡不羁的狂人——不,生活中他是一个温和的智者。

    凡是我们不理解的人,都当作是精神病算了:唯心主义是精神病,怀疑主义是精神病,尼采是精神病,一切哲学家都是精神病。当你在书店里眼睛扫过那些看不懂标题的书脊,心中是否在想:他们肯定都是故弄玄虚的骗子、自找麻烦的呆子,他们的书既看不懂也没有用。这样的世界才简单、才可爱嘛。

    令人悲哀的是我们从小受的教育就是这样子,没有试着去理解那些难以理解的人,就用一套很落后的马克思主义学说来统治我们的思想,其实马克思主义在西方早就被驳的体无完肤。

    第五章 钢铁之躯

    尼采继承了叔本华的形而上学。叔本华说物自体是“生命意志”,尼采给改造成了“权力意志”。“权力意志”一词中的“权力”容易引起误解。这并不是政治权力的意思,而是指要让自己变得更强大、更强壮、更富创造力的欲望。尼采把人分成了强者和弱者。强者体现了权力意志,他们的特征是积极向上、勇于进取、勇于牺牲、善于创造。弱者相反,特点是胆小、保守、善妒、虚伪。传统欧洲人相信基督教的普世精神和卢梭的人文主义,两者强调的都是对弱者的关怀,强调人人平等。尼采不同意。他认为,同情弱者没错。但弱者不能以此为理由,去要挟、榨取强者,去拖强者的后腿,这样做是可耻的。打个比方,强者看待弱者,就跟人类看待猿猴一样。猿猴对人类有用吗?如果不关在笼子里而和人类混居,那一定会给人类添乱。强者眼中的弱者也是一样。对弱者不应该光是怜悯,还应该限制他们的能力,免得他们给强者捣乱。

    不知道为什么,我感觉对我很有吸引力。

    尼采把道德分成了两种。他谈的第一种道德是属于弱者的道德,尼采叫它奴隶道德(又叫“畜群道德”)。表面的内容是同情、仁慈、谦卑、平等。其实本质上,是弱者为了掩盖自己对强者的恐惧、嫉妒和自私,借助奴隶道德去限制强者。比如现在社会的道德,大部分都是禁止型的命令,如“不许占有别人的财产”“不许欺骗”。这些禁令,保护的不是强者——强者不会让自己的财产被人占有——保护的是那些不能保护自己的弱者。弱者对强者感到恐惧,因此奴隶道德强调“仁慈”“谦卑”,把强者和特立独行的人看作是危险人物,要求社会限制他们的能力。弱者又因为自私就强调“同情”“分享”,要求强者给弱者分一杯羹。我们现在都讲“人人平等”,尼采却反对平等。他认为平等主义者本质是嫉妒成性,看到别人有什么,他们就也想要什么。实际上我们细想,这个所谓的“奴隶道德”,不就是我们人类社会的传统道德吗?所以尼采说:“迄今为止用来使人变得道德的一切手段都是不道德的。”尼采说的第二种道德是强者的道德,它可以叫作贵族道德。这种道德鼓励人们积极进取,特立独行,崇尚强大,鄙视软弱,追求创新,拒绝平庸,它代表了生命积极的一面。尼采认为,奴隶道德和贵族道德最明显的区别在于:奴隶道德总是在禁止,不许人们做这做那;贵族道德则是在鼓励人们自由创造。尼采并不完全反对奴隶道德,他反对的是把奴隶道德强加在强者的身上,他认为这会限制人类的发展。那有人说了,尼采的道德观不是会造成弱肉强食吗,不是会造成强者欺凌弱者吗?尼采的回答是,人的本性就是残忍的。

    感觉很有道理啊。

    尼采的道德观和基督教道德有明显的矛盾。在尼采生活的社会里,基督教道法就和咱们这里的儒教道法一样,是全社会广为接受的道德规范。《圣经》里说什么事情是善的,那全社会的人都不用多想,都认为这件事是善的。但尼采认为,基督教道德是典型的奴隶道德,本质是伪善的。基督教鼓励人们变得谦卑,其实就是鼓励人们做弱者。所以尼采大喊“上帝死了!”意思是,他想去掉上帝。如果没了上帝,人们也就不需要无条件地遵守基督教道德了。尼采反对人人平等,这和法国大革命也有一定的关系。在法国大革命中,带着民主之名的雅各宾派进行了恐怖统治和血腥屠杀,这让很多欧洲思想家看到了“多数暴政”的危险。在尼采看来,最聪明、最有创造力的人在这个社会里是少数,庸人总是多数,而原始的民主模式总是要少数人听多数人的话,这就等于让少数的聪明人屈服于庸常的大多数。尼采推崇强者,可是尼采发现,大部分强者都被奴隶道德压抑着,不能摆脱弱者对他们的束缚。因此,尼采希望“超人”出现

    原来上帝死了是这个意思。

    “超人”这个词在尼采的理论里不是指拥有强大权力的人,不是说这人一定要当总统、当将军,而是指能够完全按照自己的意志行动、能充分发挥自己的创造力,并且能摆脱奴隶道德、不被弱者束缚的强者。超人是尼采对人类的一种理想,在尼采眼里,整个人类历史里也很少有人能成为真正的超人。尼采和叔本华一样,认为这世界是悲观的。但他的解决方法和叔本华不同。尼采的世界观带有强烈的激情,他认为叔本华的禁欲是胆小者的逃避行为。他觉得人不应该像叔本华宣扬的那样避免痛苦,而是应该承认痛苦,迎战痛苦。简而言之,尼采推崇的是一种精英主义。

    在尼采看来,人类的知识,如形而上学、科学理论,都是理性的,可是作为世界本质的权力意志是非理性的,因此这些理性知识也不是真正的真理,只是权力意志构造出的假象而已。权力意志为什么要构造这些假象呢?权力意志是征服的意志,在权力意志的驱使下,人类去研究世界不是为了简单地求知,而是为了能更好地控制世界。比如,人研究世界就要给世界下定义,这些定义是人强加给这世界的,这便是权力意志控制世界的表现。既然人类的知识只是权力意志用来控制世界的工具,那么也就根本不存在什么真理。人们追求所谓的真理,只是因为人们需要合乎真理去征服世界。所以尼采说:真理就是一种如果离开它、某种生物便不能活的错误。换句话说,在尼采看来,所谓的真理和错误的区别是:真理有用,错误没用,甚至有害。比如因果律的问题,尼采的解释是,根本就没有因果律,相信因果律是因为我们不相信它就没法生活。尼采不是传统意义上的形而上学家,他的作品里没有严格的论证和推理,形式很像散文,不像是理论,更像是箴言、宣言。尼采对于西方文明的价值,不是他提出了一种新的形而上学,而是摧毁传统的西方道德观。在尼采那个时代,基督教道德是极难被撼动的。尼采喊“上帝死了”在当时是颠覆性的。如果尼采只是随便喊一喊,那他就只是一个哗众取宠的小丑。问题是,他的观点还蛮有说服力的,听上去很有道理。因此尼采起到了振聋发聩的作用。

    第八章 人猿星球

    进化论的关键内容有这么几条:第一,生物的基因信息可以遗传给下一代:第二,在遗传的时候,基因会发生不可控制的随机变异;第三,整个生物种群都面临着巨大的生存压力,每一代新生物的数量却大于自然资源能够供养的数量,因此每一代新生物中的大部分都会死掉。第四,生物后天的变化在大部分情况下不能改变基因。生物进化的过程是这样子的:因为每次遗传都会产生一系列变异,因此每一代新生物都会有一些个体的生理特征不同于父母辈。或者说,总有些个体长得“怪”。又因为生存压力特别大,每一代里的大部分都会死掉,因此假如这些长得“怪”的地方正好能适应当时的环境,那么拥有这些“怪”基因的生物就有更大的概率存活下来,这些“怪”基因也会保留下来,从而成为这个生物基因中的一部分,生物就完成了一次微小的“进化”。

    很精妙的概括了进化论。

    误解一:进化论就是生物“从低级到高级”的“进化”。实际上,“进化论”这个词不太准确。更准确的叫法应该是“演化论”。进化论的意思仅仅是,基因中那些适合环境的部分被保留下来了,不适合的部分被淘汰了。这中间并没有高级和低级之分。有些人会觉得从单细胞生物进化为人类,是从“低级”生物“进化”到“高级”生物的过程,人类比单细胞生物更“高级”。然而,人类的机体构造虽然比单细胞生物更复杂、人类的智慧比单细胞生物更高,但这并不一定就是进化的方向。假如“高级”生物指的是更复杂的生物体,并且进化论是“从低级进化到高级”的话,那么经过几千万年的“进化”,为什么今天还会有细菌、有昆虫呢?为什么比细菌构造更复杂的恐龙反倒灭亡了呢?其实,为了生存,有很多生物的构造从复杂演化为简单。达尔文在《物种起源》书里画了一张插图,画的是一棵巨大的树,树根是原始生物,越往上树的分叉越多,生物越复杂。这张图暗示了生物是从低级到高级“进化”的,生物越进化,构造越复杂。这是《物种起源》里唯一一张插图,我们的课本上过去也有这张图,然而这张图是错的。目前生物学界更喜欢的画法是把所有的生物画成一个圆形,越靠圆心的生物在地球上生存的时间越早,人类和今天所有的动植物平均分布于圆形的边缘,看不出谁比谁更高级来。这幅圆形图的意思是说,不管生物的构造是否复杂,大家都是演化的幸存者。

    确实有这个问题。

    误解六:进化论还只是一种未经验证的假说。反对进化论的宗教人士特别喜欢这么说。但有两个解释可以反驳、否定进化论的倾向。第一,所有的科学理论都是一种假说。这就是为什么我们前面要费这么大力气解释进化论的细节,我是想让大家明白,达尔文进化论是目前最合理、佐证最多、反证最少,也是最简洁、最聪明的假说。第二,有越来越多的科学发现增加了进化论的可信度。除了现实中可以观察到活生生的进化过程外,最有力的证据,是在不同地质层里发现的化石都符合进化论的预言。另一个有力的证据是人工培育。我们今天接触的大部分生物,粮食、蔬菜、瓜果、家畜、宠物,全都是人类选育出来的。今天的小麦也好,家猪、宠物狗也好,放到野外去就都不可能生存了。人工选育的过程是加速版的进化论,是进化论的应用和证据。在生物研究中,我们还会发现很多“笨拙”、无用的设计。假如生物真的是由一位最智慧的神灵设计的话,这些地方本可以被设计得更好,这也可以反驳“神创论”。

    无论是天性自私论,还是社会达尔文主义,全都犯了一个错误,那就是把“我们为何成为这样”和“我们应该怎样”等同在一起。这些观点的逻辑是,既然人类的基因是经过生存斗争而来的,那么人类就应该把这种斗争精神延续下去,继续通过竞争来筛选基因。但是,为什么呢?进化论仅仅阐述了一套基因变化的规律,这中间并没有任何道德含义。而且正是进化论把神创说从生物界赶走了,才把生物学中的道德元素降到最低的程度。在整个进化论学说中,有任何的观点能说明,进化论是道德的吗?是高尚的吗?是人类不可干涉的吗?实际上,就像人类利用力学改造自然一样,人类早已在插手生物的进化过程。这才有了不适合野外生存的家畜,才有了农作物。所以,把进化论的观点和道德连接在一起,是思维混乱的表现。

    说的好。

    第十章 寻欢作乐

    最后再说罗素干过的一件狠事。在罗素的年代,代数一直缺乏一个像几何那样逻辑完备的体系。因此数学家们创造了一个“集合论”,想给代数一个完备的公理体系。这是人类理性的一大胜利,引来当时数学界一片欢呼。然而罗素琢磨琢磨这事儿,在不到30岁的时候提出一个“理发师悖论”。大意是说,在一个小镇上,有一个唯一的理发师。他理发的规则是,只给“不给自己理发的人”理发。那么,这个理发师该不该给自己理发呢?就陷入了矛盾。我们不细究这个悖论了,简单来说,罗素用这个悖论说明了集合论的一个无法解决的缺陷。

    这是一个很有名的悖论。还有很多变种。比如说。我现在说的这句话是假话。

    最后再说罗素干过的一件狠事。在罗素的年代,代数一直缺乏一个像几何那样逻辑完备的体系。因此数学家们创造了一个“集合论”,想给代数一个完备的公理体系。这是人类理性的一大胜利,引来当时数学界一片欢呼。然而罗素琢磨琢磨这事儿,在不到30岁的时候提出一个“理发师悖论”。大意是说,在一个小镇上,有一个唯一的理发师。他理发的规则是,只给“不给自己理发的人”理发。那么,这个理发师该不该给自己理发呢?就陷入了矛盾。我们不细究这个悖论了,简单来说,罗素用这个悖论说明了集合论的一个无法解决的缺陷。

    理发师悖论很有名。

    第十一章 快乐王子

    维特根斯坦发现用不着读完书,只要交一篇论文就可以获得哲学博士学位,于是他就把那篇《逻辑哲学论》交了上去。负责审阅该论文的是摩尔和罗素。摩尔也是个有名的哲学家,当年给维特根斯坦当过老师。维特根斯坦上大学的时候水平就比得上罗素了,而且这篇《逻辑哲学论》已经成名多年,被当时很多哲学家当作经典阅读,你说摩尔能怎么评价呢?自然,他说这篇论文是“天才的作品”,水平已经超过了剑桥哲学博士学位所要求的标准。论文答辩那天,罗素和摩尔一起走进考试的房间,罗素微笑着说:“我一生从未有过如此荒谬的事。”在正式答辩之前,维特根斯坦先跟罗素和摩尔闲聊了半天。聊到后来,罗素跟摩尔说:“咱还是答辩吧,你好歹也得问他几个问题,怎么说你也是教授啊。”答辩的时候,罗素对维特根斯坦的一个观点产生了疑问。维特根斯坦解释完了,然后拍了拍两个老师的肩膀说:“别介意,我知道你们永远都搞不懂我在讲什么。”维特根斯坦这么说不仅因为他确实牛,也因为他和摩尔已经很熟了,都是好朋友。当年维特根斯坦在剑桥上学的时候,摩尔还把自己在剑桥的房间让给了他。所以摩尔回忆这次答辩的时候,说这件事“既愉快又可笑”[插图]。

    第十二章 逻辑实证主义

    逻辑实证主义者想得不错,他们要发动一场继承苏格拉底、笛卡尔和休谟怀疑精神的运动,他们要用逻辑工具去一一考查所有的哲学命题,把所有没有意义的、不可证实的命题都剔除出去。然而工作的结果却吓了他们一跳。他们发现,剔除到最后,只剩下了两种命题。一种是重言式命题,就是类似于“桌子是桌子”这样的话。这样的话当然是绝对正确的,可是这样的话不包含任何有用的信息,不过是文字游戏而已。之前的形而上学家们找到的那些所谓终极真理就多半属于此类。因为重言式命题绝对正确,所以就会被误以为是终极真理。这些终极真理一点用也没有。还有一种命题,是类似“这朵花是红色的”之类描述片段经验的命题。虽然是新知识,但是无法形成普遍真理,也就无法回答哲学问题了。其他的哲学问题,特别是形而上学问题,全都是没有意义的伪问题。要么违反了种种逻辑规则,要么无法用经验去实证。这样一来,逻辑实证主义者就回答了一个问题:为什么哲学家们对形而上学争论了那么久都没有结果呢?因为他们争论的全都是没有意义、不可能有答案的问题。人们没办法靠实证的方式来解决这些问题。

    年轻的维特根斯坦写了一本《逻辑哲学论》,完成了逻辑实证主义的工作。写完这本书以后,维特根斯坦以为自己解决了所有的哲学问题。语言都被他用逻辑工具分析光了,他觉得所有用语言能表达的句子他都明白是怎么回事了。所以维特根斯坦说:“凡是可说的事情,都可以说清楚,凡是不可说的事情,我们必须保持沉默。”对这句话我的理解是:凡是符合逻辑实证规则的语言,内容都很清晰准确;凡是不符合逻辑实证规则的语言,说了也是没意义的,就不用说了。这么一来,维特根斯坦觉得他没有困惑了,就去乡下当小学老师了。但是随着时间的推移,他逐渐觉得不是这么回事儿。维特根斯坦在剑桥当老师时他学生的课堂笔记,以及他留下的那些思考笔记,都在他死后被他的学生整理集结成书出版了。这本书显示了维特根斯坦在《逻辑哲学论》之后的哲学思想和逻辑实证主义完全不同。

    维特根斯坦发现,语言并不能只停留在表面的逻辑分析上。同样的一句话,说话的情境不同,说话人的语气、表情、手势不同,常常会表达出不同的意思。换句话说,每一个情境都给语言制定了不同的规则,语言得和规则结合在一起,才能显示真正的意思。而这规则又是没有逻辑可言的。维特根斯坦揭示的,其实是理性思维和现实的矛盾。逻辑实证主义的理想很好,要坚持绝对的理性、绝对的正确,可是最后发现,这个绝对的理性却得不到任何有意义的结论,连一个普遍的理论都得不出来。可是另一方面,我们的现实生活是多姿多彩的,我们有灿烂的文化,有日新月异的科学知识。这说明了什么?说明理性根本无法担负从总体上解释世界、指导生活的任务。维特根斯坦建立的逻辑实证主义,又被他自己亲手毁灭了。可以说,维特根斯坦是世界上独一无二的、能够提出两种截然不同又都对哲学史影响深远的理论的哲学家。所以我们前面才说,他是史无前例的双倍哲学家。

    第十四章 终结形而上学

    波普尔看出了其中的问题,提出了一个检验科学理论的重要标准:证伪。什么是科学理论,什么不是?其中关键的标准,是看这个理论有没有可以被证伪的可能。具体来说:科学理论必须能提出一个可供证伪的事实,假如这个事实一经验证,便承认该理论是错的。如果暂时没有人能证明它是错的,那它暂时就是真的。比如“所有的乌鸦都是黑色的”,这就是一个可证伪的命题。这等于说“只要你能找到一只不是黑色的乌鸦,就能说明这个命题是错的”。既然我们尚未找到不是黑色的乌鸦,那么到目前为止这个命题就是暂时正确的。换句话说,所有的科学理论都是一种假说,科学家没有办法证实任何一种科学理论[插图]。但是科学理论可以给别人提供验错的机会。在没被检验出错误之前,我们就姑且相信这个科学理论是正确的。

    证伪主义有点像是科学理论上的进化论。在形而上学统治的科学观下,人们认为存在着一个绝对真理,我们在形而上学的指导下,可以带着科学大踏步地朝着这个真理前进。证伪主义的科学观是,人类提出的各种科学理论有点像是基因突变,科学家们发散思维,想出各种充满想象力的假说。证伪就如同自然环境对基因的筛选,经不住证伪的假说都被淘汰,留下的都是经得住检验的,也就是暂时正确的科学理论。那些留下来的理论,科学家们也在不断地尝试证伪,一旦证明是错的,就进行修改。这样科学理论就会越来越完善。这个试错、修改、完善的过程是无休止的,科学也因此会越来越接近真理[插图]。概率主义认为,我们每一次检验科学理论正确,都是在为科学作贡献。证伪主义认为,检验正确并不为科学作贡献,只有检验出科学理论是错的,才是真正为科学作贡献。

    第十四章 终结形而上学

    现在,世界大部分国家的刑事司法都接受“无罪推定”原则。意思是说,假如没有足够的证据证明一个人是犯罪嫌疑人,那么就应认为他是无罪的。为什么要坚持这个原则呢?除了人权精神外,还可以用证伪主义来解释。如控告某人参与了一起诈骗,如何证伪这句话呢?首先,被告人必须找出被控告这段时间内的所有活动细节,从而证明自己没有和诈骗团伙有过联系。且不说和团伙有过联系又没参与诈骗的人该怎么说吧,就说真没联系过,他又该怎么向法庭彻底证明这一点呢?证明自己没出过门、没见过犯罪嫌疑人?那你有没有可能用电话联络?你没用电话,那有没有用过电子邮件?电子邮件没用过,那你用没用过飞鸽传书,用没用过烽火?你如何证明自己没有在被控告的时间内使用过烽火?找来邻居证明你们家那几天从来没冒过烟吗?邻居说我中午打了一个盹,没看见,那你就算有罪啦?“某人犯过某罪”不可证伪。相反,“某人没犯过某罪”,这个命题是可以证伪的。只要找到他犯罪的证据就可以推翻这个假设了。因此,在都没有充足证据的情况下,在两个命题中,法院只能采信可证伪的后者,而不会采信前者。前面还提到过“判例法”。在该制度下,法律不是一成不变的,而是每一次审判都会对法律的解释进行修改,法律在审判中不断改进自己,以便更适应新的社会环境。这也符合证伪主义“没有绝对的真理,理论需要永远不停改进”的真理观。

    想想三法印,都是可以证伪的。第一条诸行无常,如果找到了一个能够永恒不变的事物,它就被证伪了。第二条诸法无我,如果找到一个事物,不是由其他事物构成的,而是有一个永远不变的自我构成的,它就被证伪了。第三条涅槃寂静,如果有圣人达到涅槃,而不是处于寂静的状态,这一条就证伪了。所以佛教是很有科学精神的。😃

    历史主义的逻辑是,既然自然社会存在规律,那么历史也应该有规律。我们历史主义者像科学家一样揭示了这个规律,人类按照我们揭示的规律奋斗就可以了。但证伪主义认为,没有永恒不变的真理,所有的理论都可能是错的。所以,也就不存在什么“历史的必然规律”。而且科学理论未来的发展方向也是难以预测的。就比如在牛顿时代,没人能够预测相对论的出现,也没人能预测牛顿理论将会在哪里出问题。因此,预测未来的历史规律,一劳永逸地设计一种绝对正确的政治制度,也是不可能的。用钱穆先生的话说:“制度须不断生长,又定须在现实环境要求下生长。”波普尔因此主张应当建立“开放社会”,要求执政者能够广泛接受意见,赋予大众质疑政策的权利。因为执政理论和科学理论一样,永远都可能是错的。必须要不断地接受证伪,才能保证理论的正确。这正是现代民主思想的核心精神。我们有的人可能会简单地以为,民主就是“大家一起投票,多数说了算”,就是“少数服从多数”。其实这种原始的民主制度有极大的缺陷,这个缺陷在雅典人判苏格拉底死刑、法国大革命的屠杀、希特勒被民众选上台等事件中已经暴露无遗,早就被现代社会抛弃了。我们常说“人民大众的意见最正确”,这句话对吗?在证伪主义看来,这话就有问题。因为证伪主义认为世上没有绝对真理,那怎么可能说某个意见“最正确”呢?就算全世界99%的人同意的一件事,也不能说这件事最正确。否则,布鲁诺时代就不用怀疑地心说了。证伪主义的政治观,最关心的不是谁制定的政策,而是无论谁制定的政策,都不能成为绝对真理。不管是美国总统下的命令还是全世界人民投票的结果,都要给别人留出修改、推翻它的机会。

    中国正在努力的远离开放社会,😂

    无论谁被民选上台,也不会给世界造成太大伤害。因为他上台后的个人权力非常有限,哪怕加个税都需要国会批准。他还必须随时面对全国媒体的质疑、随时可能被弹劾、干四年就得重选、干八年就得下台。这制度不能保证总统想出“最正确”的决策,但可以保证一旦总统作出“错误”的决策,举国上下有无数可以阻止它的机会。可以随时“纠错”而不是“多数说了算”,这才是现代民主制度的核心精神。

    看来我以前对民主制度的理解是错的,我以前以为民主制度是让民众有选择的权力,就像那个故事里面一样,你是愿意选择一头狼,还是愿意选择两头狮子统治你。

    历史主义的另一个问题是,整个人类历史发展所涉及的因素太多,我们无法设计实验,让历史大事件重复发生。因此,哪怕是对已经发生过的历史,很多解释也是无法证伪的。比如有人说:“历史是由人民创造的,假如没有法国人民的力量,拿破仑就不可能成功。”有人说:“历史是由伟人创造的。没有拿破仑,就不会有法兰西帝国。”这两个命题其实都是不可证伪的。因为历史不可重复检验,谁也不能让时光倒流,把拿破仑用飞碟抓走,再重新检验一遍历史。也不可能现在做一个实验,模拟拿破仑时代的所有经济、文化、政治细节,来检验这两个理论。因此这两种观点可以永远吵下去,各自举出无数的间接论据,却全都无法说服对方。所以,历史主义都是不可证伪的空话。历史主义者对未来的预言如果是明确的(如预言某某事件在某个时间段一定发生),那的确是可以证伪的。但这需要理论持有者能勇于承认错误,一旦理论被证伪了就要认错,而不要推脱责任,说额外因素太多,干扰了预言。但事实是,现实中能够干扰历史进程的元素太多了。有很多历史主义的信徒,不断用各种额外出现的新因素解释原有理论的错误。这时,历史主义也就变成不可证伪的了。因为影响历史的因素太多,不可重复实验,因此波普尔认为,科学只能研究局部的社会问题,如某条法律好不好用,某个政策价值如何。因为只有局部问题才可以反复验证。而那些执掌全局的宏观理论,都是不符合科学精神的。

    我总算找到我为什么一直觉得宏观经济学是扯淡的原因了,原来是有科学道理的。

    第十五章 实用主义的科学

    科学是个只讲究实用与否的工具。我们在筛选科学理论的时候,实用是唯一的标准:首位的要求是这个科学理论能够指导我们工作,不能够出错。其次,在不出错的基础上越简单易用越好。就比如牛顿力学其实是错的,但在我们粗糙的日常生活中已经足够用了,我们就只当牛顿是正确的,不需要了解相对论。而且相对论就绝对正确了吗?也不是。相对论也是可以被证伪的。目前没人推翻相对论,只是因为人类现在观测到的物理数据里还没有足够多的和相对论违背的数据。等到人类的观测能力不断提高了,有朝一日违背的数据越来越多,相对论不够实用了,就会有新的理论去代替相对论了。假如你接受这一点,那么可以听听我个人给科学下的定义:科学就是建立在经验主义基础上的、以实用主义为原则筛选出来的、可以被证伪的理论。说白点就是,科学就是我们在一堆科学假设中,挑出一个能够解释已有的实验和观测数据,而且表述尽量简单,而且还可以被证伪的理论。这个理论就是最“科学”的。

    第十六章 科学是什么

    虽然我们不能把科学当作衡量一切理论的标准,但是我们仍旧有标准可以用。我认为有两个原则必须坚持:第一是经验主义原则。换句话说,理论好用不好用,必须眼见为实,拿出大家都承认的证据来。第二是实用主义。理论还得有实用价值,不实用的理论再诱人也没有意义。

    我们选择信不信某个理论是为什么呢?比如,我们为什么需要在民间医学和现代医学之间选出一个更优秀的理论呢?因为我们要治病对吧?我们要的是它的实用效果。所以我们关心的是这两个理论哪一个更实用。假如你去找了一个懂民间医学的人,他给你看完病说:根据我的某某理论,你的病好啦。你会就这么相信他吗?不会吧。你得在以后的日子里观察自己的身体,看自己的病是不是真的好了。要是没好,你就会找他算账去。所以,我们要选择理论,原则必须是实用主义的,依据必然是经验主义的。因此,拿经验主义和实用主义做考察理论的标准,这不是出于科学家的学霸作风,而是出于我们自己的需要。如果我们接受了这一点,回来再看科学:科学坚持经验主义、坚持实用主义,并且完全开放,允许证伪、允许质疑,反对独断论。那么,还有什么研究方法能比科学更好呢?所以我的观点是这样,我们不能说某个理论“不符合科学理论”,就认为它是错的。但假如我们认为“科学方法”指的是“以经验主义为标准,以实用主义为目标,允许别人检验,反对独断论”的话,那么我们就应该相信科学,就可以说:如果某个理论的论证过程“不符合科学方法”,那么它就是不可信的。

    以现代医学的标准,我们的传统中医有很多地方是“不科学”的。中医的辩护者们有一个论点,说你们西方人凭什么非要用“科学”的标准去看待我们中医?那是你们的标准,不是我们的。你强行用自己的标准要求我们,这是一种霸权主义。把科学不言而喻当作衡量事物的标准,这不也是一种迷信吗?这个观点我同意。没错,现代西医中的种种理论、观点都不是绝对正确的,现代西医仅仅是我们对人体的众多解释中的一种,绝不是唯一的。特别是对于人体这种极为复杂、经常处于变化中的研究对象,或许中医的确比西医有更大的潜力,这些观点都是没问题的。但是关键一点,我们不能放弃经验主义和实用主义。就是说,如果想证明中医比西医更有效,就必须在大范围内进行治疗实验。目前最好的方式是大样本随机双盲实验。做完实验一统计,对于某个病症,哪种治疗方法的效果更好,我们就选择哪种疗法。说中医历史上有过多了不起的记载,里面有多少五行八卦之类深奥的哲理,中医“能平衡人体”“从整体看待人体”“更自然”等等理由,我认为都不重要。重要的是此时此地,它能更有效地治病,它能切实地延长人的寿命。你能实现了就算你本事,你要是不能实现,你的理论再天花乱坠我们也弃你不用。对于西医来说,我们也同样对待。

    最好的检验办法还是“大样本随机双盲实验”。解释一下。“大样本”是说,我们验证某个方法可靠不可靠,光看一个例子是不行的,要找很多很多的例子一起验证。例子越多,实验的结果就越可靠。“双盲”是说,要把实验对象分成好几组。比如实验一个疗法是否有效,不能光看吃了药后多少病人病好了。还应该有另外一组病人,只给他们毫无疗效的安慰剂,对比两组的实验结果,才能知道药物的真正疗效。病人的分组应当是随机的,保证各个组之间患者的情况类似,不能身体好的分一组,身体差的分一组,那样就作弊了。而且这个过程,被实验者和实验者都是“双盲”的。安慰剂的外形和真实的药物一模一样。吃药的患者不知道自己吃的是真实的药物还是安慰剂,目的是避免患者的心理作用影响结果。同时,亲自给患者服药的医生,也不知道哪些病人吃的是真药。这样做是避免医生通过心理暗示(如对服真实药物的患者更关心)来影响结果。当然,最终实验的统计者能准确知道哪些病人吃的是真药,哪些吃的是安慰剂。有的实验做不到“双盲”,也可以只做“单盲”,只瞒着病人。大样本随机双盲实验的作用,在于寻找两件事(如实验药物和疗效)之间真实的因果关系,尽量排除其他因素的干扰。比如,有一些病是可以自愈的,还有很多病只靠心理作用就可以加速痊愈。有很多人一有病就吃药,吃药后身体果然好了。这到底是药物成分的作用呢,还是药物的心理安慰作用呢,还是纯粹是身体自愈了呢?要想搞清楚这些,最好的做法就是找大样本病人,分成三组,一组吃药,一组吃安慰剂,一组不吃药,双盲实验,统计结果。药物有没有用一目了然。

    星座算命所给出的结论大多是不可证伪的。但有时也有一些可以证伪的结论,有时也灵,那怎么知道星座到底有没有用呢?找一堆人,随机分组,一组人用和本人相符的星座的预测结果,另一组用和本人不符的星座的预测结果,但也告诉他们这些都是真实的预测结果。看看两组人觉得灵验的比例是否相近。实际上,有人已经做过这样的实验,把同样一段预测结果发给不同星座的人,结果人们都表示很“灵”。双盲实验在这个问题上,排除的是“巴纳姆效应”的干扰。这是一个心理学效应,说的是人们倾向于相信为自己量身定做的、模糊的性格预测,而不管这个预测是不是真的准确。

    我们在历史上常看到某些特别灵验的算命大师。如在某名人年轻的时候说此人大有可为,如在朝代更替之前就能预言某朝当兴,假如我们翻开史书统计,灵验的预言比不灵验的要多很多,这说明算命真的有道理吗?不能,因为样本不是随机选择的。要真的检验算命,应该找一大堆算命大师,让他们作出大量可证伪的预言。再叫不会算命的人用类似的句式作一些假预言,成为对照组。看看是不是算命大师组预言为真的概率明显高于对照组,甚至于只要其中有某一个大师的预言概率明显高很多也可以。否则,哪怕名声再大、口碑再好的算命大师,无论历史上的还是当代的,都不能算他真正有本事。双盲实验在这里排除的是“幸存者偏差”的干扰。这个意思是说,只有被验证为正确的预言,人们才会广为传诵,才愿意记录在历史书上。那些出错的预言,人们没兴趣传播。因此光从历史书上或者邻里的传闻里听说的某大师灵验所留下的印象,和现实是有偏差的。

    认知心理学真的要好好学一学,我们每个人的心里都充满了偏见。

    大样本实验的成本非常高,药物实验耗时很长,很多治疗方法没有条件接受这样的实验。严格如美国的食品和药物管理局,批准新药上市也不是全靠双盲实验,还要结合临床医生的反馈等等更多的信息。但是,这里一定要说但是,对于那些争议特别强的理论,大样本随机双盲实验仍旧是剔除骗术、心理误差、第三方原因等非常态因素来检验争论对象是否有用的最好办法。没有其他任何方法更有效了。说这么多,并不是要具体下结论说中医如何,而是用这个有争议的话题来说明,无论任何声称自己和科学“不是一个系统”、不能“把科学理论强加给我”的理论,最终还是离不开实用主义,离不开经验检验。而且最好的检验办法,就是大样本随机双盲实验。假如你拒绝检验,那么你的理论无论中医还是西医,在大部分情况下就是不实用的,很难和信口开河的巫术、骗术区分开来。

    在我眼里中医其实就是巫术或者骗术,鲁迅当年就说过中医都是有意或者无意的骗子。

    大样本实验的成本非常高,药物实验耗时很长,很多治疗方法没有条件接受这样的实验。严格如美国的食品和药物管理局,批准新药上市也不是全靠双盲实验,还要结合临床医生的反馈等等更多的信息。但是,这里一定要说但是,对于那些争议特别强的理论,大样本随机双盲实验仍旧是剔除骗术、心理误差、第三方原因等非常态因素来检验争论对象是否有用的最好办法。没有其他任何方法更有效了。说这么多,并不是要具体下结论说中医如何,而是用这个有争议的话题来说明,无论任何声称自己和科学“不是一个系统”、不能“把科学理论强加给我”的理论,最终还是离不开实用主义,离不开经验检验。而且最好的检验办法,就是大样本随机双盲实验。假如你拒绝检验,那么你的理论无论中医还是西医,在大部分情况下就是不实用的,很难和信口开河的巫术、骗术区分开来。

    很多国外采风里说,西方人做饭跟做科学实验差不多,跟你学做饭的时候,非要问清楚“少许”葱姜是多少克、“少许”酱油是多少升,厨房里连量杯都用上了。可以说,他们的做菜方式是非常符合“科学”标准的——实际上,好吃又便宜又能快速开分店,不依赖厨师技术还能保持味道不变的西式快餐,就是这么控制质量的。制作菜肴的每道工序都有近似于科学实验的严格数据控制的要求。但是我们都知道,外国人照这种方式做出来的中国菜并不好吃。而一个中国人,完全没有“科学”的学习过程,只靠纯经验的积累,练上一两年就可以做出美味的中国菜来。无论是中国人还是外国人,都喜欢吃中国师傅按照这种“非科学”方法做出来的菜,这就证明,在做中国菜上,中国烹饪法是优于“科学”烹饪法的。实际上,也并没有科学家跳出来冲着中国厨师说:你的做菜方式是不科学的,所以你的菜再好吃也没用,我不吃!这可以证明:科学并不一定就是解释、改造世界的唯一标准。比如在做菜这件事上,科学方法就被打败了。而且我们评价两者孰优孰劣的标准是非常清晰的:立足经验主义的实用主义。中国烹饪法做出来的菜好吃,技巧容易掌握。所以就赢了。

    有一种观点,说科学只是众多认识论中的一种,只相信科学,拒绝别的理论,不也是一种迷信吗?什么叫“迷信”呢?不经思考的相信,不允许别人质疑,就叫“迷信”。假如一个人在没学过哲学史的情况下,认为科学代表了终极真理(在哲学中叫作唯科学主义),不承认科学的局限性,认为不能证伪的观点就是错的(我认为,不能证伪的命题仅仅是不可知的),那么这的确可以称作“迷信”。但是,如果他了解科学的局限,仍旧相信科学的结论,那就不叫迷信了。在这点上,我认为科学和宗教、巫术不是对等的,科学比宗教和巫术更“不迷信”一些。关键不仅在于科学理论可以证伪,还在于它的检验是开放的。科学理论的语言基于严谨的逻辑,任何人只要花一点时间学习都能读得懂,科学没有权威(有的宗教教义只有神职人员才有权解释,教众不许有不同的说法),任何人只要有技术条件,都可以去证伪、推翻最权威科学家的理论(有些宗教拒绝教外人士参与讨论,而科学不会歧视人的身份,只要你拿出证据来就行)。因为这些原因,科学虽然有局限,但科学比其他不允许质疑的理论,要更“不迷信”。

    以前别人问我只相信科学是不是也是一种迷信,我经常没办法很好回答,以后可以把这段话牢牢记住,然后回答别人了。不过很多时候别人这么问的时候,就已经表示他不想接受你的回答了。他就是要相信别的,比如说中医,比如说宗教,就像那句老话说的,你没有办法叫醒一个装睡的人。不过我相信把这段话好好记住,并且用自己的话讲出来,应该比我以前讲的那一套更有说服力。这里我试着用自己的话讲一遍。如果别人以后问我相信科学是不是一种迷信,我就这样回答他。首先你说什么叫做迷信呢?是不是不加思索的相信。如果这样说的话,那么科学就是最不迷信的。因为科学是没有权威的,同时它是鼓励你怀疑的。它是基于经验主义和实用主义的。科学没有说它自己是真理。它只是说它是最实用最经得起检验的理论。它随时准备着推翻自己。只要有新的证据出来,它就承认自己是错的,然后去接受更新的,能够解释新证据的理论。相比之下宗教巫术或者中医这些东西,要么经不起检验,要么不实用,所以我们说它不科学,我们没有说它是错的,只是说他不科学而已,就是说它经不起检验,或者没有用。比如宗教,你说有上帝,那你让它造个电视机给你看看。比如说巫术,那你让他预言一下明天天气怎么样,有天气预报好使吗。比如说中医,你让它分辨一下什么是病毒,什么是细菌试试。所以科学只是最靠谱的一个理论而已。

    世界各大宗教内部都分成很多小派,不少小派之间也互相攻击,互相骂战。为什么宗教总讲“宽容”“慈爱”,而这些细小的派别却难以统一呢?因为宗教理论大多不可检验、不可证伪,因此不同的观点根本无法辩论出对错。过去,教会要靠宗教裁判所这样的暴力机关才能解决争议。可你没听说过科学也有科学裁判所,会把持不同科学结论的人抓起来判刑吧?科学家和其他人类一样拥有各种阴暗面,一些科学家也自私,也互相嫉妒,也会虚伪欺骗。科学家在研究同一件事的时候,也常常各执己见,都认为自己是对的,谁也不服谁,甚至还会有李森科[插图]这种利用政治权力打击异己的恶劣事情发生。但是科学的方法是开放的,因此不同的意见哪怕相隔万里、相隔千百年,也都可以在同一个平台上公平对话。任何人都可以通过实验来发表自己的意见,时间长了,对同一件事检验的次数多了,自然就会分出正误来。当年有多少人不服牛顿,天天和牛顿打架,不久以后,就再没有科学家否认牛顿,因为如果你在否认牛顿的基础上研究,你就不可能搞出任何经得住经验检验的成果来。同样,当年有不少人反对爱因斯坦,过了一段时间后,那些反对的声音也都渐渐没了。教会当年用了成千上万个宗教裁判所、遍地而起的火刑架都没能统一观点。科学家们只靠着几本学术期刊就搞定了。这不是非常了不起的事吗?很多宗教人士都乐于使用汽车、飞机、手机、电话等高科技产品。不为什么,就因为这些东西是最实用的。在一百年前,传教必须靠步行万里路,站在街头扯着脖子喊。现在传教可以用鼠标一点,网页上一发,几十万人都能同时看到。这样方便的技术,为什么不用?我说这些话并不是在攻击宗教,因为使用这些科学产品和世界主流宗教的教义都不矛盾。

    这本书真是处处都要画重点。

    第十七章 永恒的终结

    形而上学走不通,形而上学的问题都没有答案。记住这一句话就够了。我们说过,形而上学的任务,是用理性思维去研究世界本质等“大问题”。形而上学走不通,也就是说,理性不可能回答“世界的本质是什么”“有没有终极真理”“终极真理是什么”“人生的意义是什么”等大问题。硬要回答,答案一定是独断论的,或者在推理上有错误。形而上学家们研究了好几百年,就得出这么一个结论。实际上,所有的形而上学都会陷入无法证明自身的困境。我们说过,经验主义者们的论断“只有来源于经验的知识才是可靠的”,并非来自于经验。康德用来批判理性的工具却没经过自己的批判。黑格尔讲辩证法,但是他的辩证法到最后却并不辩证。尼采说所谓的真理都是谬误,那他自己的理论不也是谬误了吗?逻辑实证主义用来分析语句的规则,经过自己的分析都变成无意义的了。波普尔的证伪主义理论,是不能被证伪的。后来到实用主义的时候,罗素批评说:实用主义以“是否实用”为标准评价真理,但是“是否实用”的标准是什么呢?如此追问下去,必然会形成无限回溯,得不出结论。我们会发现,这种情况在哲学史上不是偶然,几乎每一个哲学流派,都面临着自己不能证明自己的窘境。那么,这种“怀疑者不能怀疑自身”的质问只是一种抬杠吗?如果我们是怀疑者,那我们把原则改成“我们可以怀疑一切,除了本原则之外”不就可以了吗?不行。因为哲学研究的是“什么知识真实可信”的问题,是认识论的问题。按照怀疑精神,任何知识必须先确认是可信的,才能被我们接受。然而,我们用来确认知识是否可信的方法(也就是各种哲学理论),本身也属于知识的一种,它们在给别的知识提出限制的同时,也就是在给自己提出限制。形而上学的任务之一是保证一切知识的来源是可靠的,如果它连自己的可靠性都不能保证,就正好说明它是独断论。

    重要的话说三遍。理性不能回答世界的本质是什么。理性不能回答世界的本质是什么。理性不能回答世界的本质是什么。所以对于世界的本质这个问题,我们一定要找到一个立足点,哪怕这个立足点可能是非理性。作为一个佛教徒,我觉得这个问题回答的最好的还是佛教。佛教的三法印都是经得起检验的。一切事物都是无常变幻的。一切事物都是由其他事物构成的,没有永恒不变的本质。超越一切事物的涅槃是寂静的。这三法印里面,前面两条是完全基于理性的,第三条其实是有些非理性的。其实从佛教的观点来说,涅槃是无法用语言来描述的,因为涅盘超越了时间和空间的概念,也超越了理性的概念。如果从理性的角度出发,我们其实无法证明存在涅槃,也无法描述,甚至讨论涅盘。所以我们一定要在某种程度上非理性的相信,涅槃是万物的终点,也是我们的终点,当然这里所谓的终点并不是真正的终点,我只是用了这个词而已。

    不靠理性去评价宗教、理学、心学等非理性理论,理论上是不错的,但实际上很难行得通。

    神学家奥托说,宗教信徒在强调“神迹”的时候,实际上还是在用理性评价宗教。因为所谓的“神迹”是建立在相信理性的基础上的。违反常规的东西才是神迹,而“常规”是个理性概念。对于非理性的宗教信徒来说,不存在常规不常规,也就不存在所谓的“神迹”。拿佛教来打个比方,假如佛凭空变出了一座金光闪闪的大山,那一般人(包括我)都会跪下来说:“太厉害了,这就是神迹,我信服了!”可是对于真正的佛教徒,他会按照佛教的教义,认为世间一切事物的本质都是超越理性的,是“空”,神迹本身也是“空”,那有什么好大惊小怪的?但是,对于大部分普通人,还是会大惊小怪,还是会很在意“神迹”吧?也就是说,从理论上,宗教信仰的确是超越理性的,可以拒绝理性讨论。但与此同时,包括宗教信徒在内的大部分人,都不可能离开理性思维。如果有一个人,一面告诉别人信教有多少好处(“好处”就是一个理性概念)、告诉别人有多少“神迹”可以作为宗教真实的证明(“神迹”“证明”都是理性概念),与此同时,在辩论不过别人的时候,便用“不能用理性来评判宗教”当挡箭牌,拒绝理性讨论。我认为这样是双重标准,是不妥的。

    粗览艺术史不难发现,顶级艺术家都很苦闷。作为这世界上最有才华的人类,顶级艺术家思考的问题常常和哲学家一样,都是一些形而上的终极问题。只不过艺术家不用理性探索,而是想通过艺术作品让别人和自己感同身受。但他们为什么都不约而同地苦闷呢?他们不都是最聪明的人吗?他们不都是在用毕生的精力追求答案吗?原因只有一个:终极问题没有答案,最聪明的人们追求到最后,不约而同地发现这是一条绝路。但正是因为这些艺术家陷于永远无法挣脱的苦闷,而他们又非要倚仗自己过人的天才全力挣扎,所以他们的作品才能深深打动我们。所以世界上才有艺术这东西。

    好吧,不知道为什么,我对艺术一直有一种负面态度。可能因为我对艺术是门外汉,也完全欣赏不了艺术,在我的感觉里面,所有的艺术都是装腔作势。我感觉所有的绘画都赶不上照相机,而且我也不太分辨得出来哪些照片照的好,哪些照片照的不好,最多也只能分辨出哪个照片稍微好看一点,以及照片是不是模糊。对于音乐也是一样,我只能大概的分辨出哪些音乐比较好听,一般来说对我来说听得次数多的就比较好听,可能仅仅因为我听顺耳了。文学也是一样,在我眼里所有的文学作品全部消失,这个世界也不会有太大损失,只要保留科技方面的文字就够了。有可能仅仅是因为我是个粗人吧?

    第十九章 西西弗的神话

    加缪不愿意媒体把自己的名字和萨特放在一起,也不认为自己的观点属于“存在主义”。但是大众基本不理他这茬儿,现在一般人谈到加缪,都认为他的哲学属于存在主义。他最有名的观点是,世界是荒谬的。这是什么意思呢?假如这个世界上有终极真理,那么就意味着在这个世界里有某种高于一切、比任何事物都重要的东西。那么人的存在就是有目的的,目的就是找到这个最最重要的真理,或者按照这个真理的指导来生活。这就是形而上学下的人生意义。过去的形而上学家们,在相信自己发现了真理后,都认为人生是有目的的。比如宗教信徒认为,在教义的指导下生活就是人生目的;叔本华认为,对抗生命意志是人生目的;尼采认为,努力当超人是人生目的;黑格尔更是认为整个历史都是有目的的,个人的人生目的是去努力实现历史的目的。可是,当形而上学不存在以后,这些目的就都不存在了。这世上没有终极真理,那就没有什么必须无条件为之奋斗的目的,那么,人到底为什么活着?人来到这个世上也不具备什么特别的目的。萨特就说,世间万物的存在都是偶然的。也就是说,稀里糊涂,没有任何理由,人类和世界就这么存在了。总而言之,人生也好,世界也好,没有任何目的。

    按照佛教的观点,无明也是没有目的的。

    问题是,人类能理解的故事有一定的固定模式。这个模式经过人类文明的千锤百炼之后,早就固定下来:故事必须有开头,有情节,有高潮,有结尾。任何一个能被大众接受的、听着比较“正常”的故事都得有这几个要素。试想,假如我们给别人讲一个没有开头的故事,我们说:“小王,我跟你说个事,那两个人后来成好朋友了……”小王会立刻打断我:“等等……你说什么,我没听懂。”小王为什么会拒斥这个故事呢?因为没有开头的故事对他来说,不可能提供有用的信息,他没有办法理解。再试想,假如一个故事没有高潮,或者没有结尾,那会怎么样呢?我们给别人讲一个故事,讲到最关键的时候突然停下来不讲了。那个人就会忍不住问:“继续讲啊,然后呢?”如果我们不讲,他甚至会生气:“你这个人怎么这样呢,说话说一半!”为什么他会生气?因为他的理性思维难以接受一个没有解决冲突和悬念的故事,甚至会因为过于难受而感到愤怒。为什么世界各地、各种年龄、各种文化背景的人都愿意去看好莱坞电影,看完之后都会心满意足?原因之一,是好莱坞电影的故事严格遵守开头、情节、高潮、结尾的故事模式,这样的模式符合人类对故事的预期,这个预期是全人类共有的。我们对整个世界的了解,都建立在一个有头有尾、有情节、有高潮的故事的基础上。

    第二十章 人生的意义

    该如何找到自己的人生意义?我认为最有效的办法,是逼迫自己直面死亡。我们问人生的意义是什么,其实就是在给自己的人生找一个目标,就是在问:“我为什么活着?”这也就等于在问:“我为什么不立刻自杀?”加缪说过:“真正严肃的哲学问题只有一个,那就是自杀。”维克多・弗兰克是一名奥地利犹太人。二战的时候他被关进纳粹集中营,经历了地狱般的磨难后侥幸逃生。战后他成了一名心理学家。他在治疗的时候常问病人:“你为什么不自杀?”因为借助病人的回答,他可以“为一个伤心的人编织出意义和责任”。假如你能顺利地回答“我为什么不自杀”的问题,如“我不想死,是因为我还想到处旅游,吃好吃的”“我不想死是因为我不能让父母伤心”。那么,这些答案就是你现在的人生意义。假如你的回答是“我不觉得活着有什么意义,我只是怕死”呢?那就请你想象一下死亡来临时的感觉吧。一个无神论者在面临死亡的巨大恐惧的时候,有时,求生的本能会让头脑拼命地给自己寻找活下去的理由。这个理由,也就是每个人的人生意义。有些和死神擦肩而过的人说,经过这一场磨难,自己大彻大悟,对人生有了更高层次的看法了。可是,我们每个人都知道自己早晚会死,为什么非要死到临头的时候才会大彻大悟呢?那就是因为绝大部分人平时从不愿意直面死亡,潜意识里认为自己可以永远逃避死亡。所以只有死到临头,才会开始反省人生。我们既然知道了这个道理,那就不妨早一点直面死亡,早一点把这件事想明白。怕死还意味着我们要珍惜生命。

    作为一个佛教徒,我还比较好回答这个问题,我不自杀是因为自杀违反了佛教的纪律,佛教的根本五戒之一是不杀生,杀死自己也是杀生,所以不可以做。对于死亡,我是不怎么害怕的。从佛教的观点来看,出生和死亡只是相对意义上的事情,在绝对意义上,并没有出生和死亡这回事,我们的本性,或者说是佛性,或者说是世界的真相,或者说世界的本质,或者说是至高无上的真理,是超越时空,超越一切概念,也超越死亡的。所以在绝对真理面前,死亡只是一件微不足道的小事。即使在相对意义上讲,死亡也难以定义,我们每一个瞬间都在死亡,或者说是迈向死亡,到最后真正死亡的那一瞬间,我们应该是回归了绝对真理的怀抱。当然回归怀抱这些词都不准确,但是没有办法,我总要用人类的语言,除了人类的语言以外,作为人类,我没有别的语言可以用。所以归根到底,对于死亡我既不期待,也不讨厌,总有那么一天我会死去,但是我无所畏惧。在死亡之前,我希望能够通过修行在这无明造成的轮回中,偶尔能够瞥见绝对真理,相信如果能够在活着的时候能够瞥见甚至亲身体验绝对的真理,我一定能够对死亡后的情形更有信心。就目前而言,虽然我偶尔能够体会前所未有的平静,但是我不确定那是不是绝对真理,同时我觉得所有宗教里面描述出来的天堂地狱一定不是绝对真理,绝对真理一定是超越这些文字描述的,而且应该是超越物质的。虽然没有任何证据,但是我对这个超越概念,超越理性的绝对真理有无比的信心。正因为这个信心,我才能对死亡这么淡定。

    点评

    ★★★★★
    风趣幽默,通俗易懂。关于西方哲学,如果你只想看一本书,我觉得这本就够了。我以前也看过一些其他的西方哲学方面的书,印象都很模糊了,感觉里面的概念太多,读起来太烧脑。这一本就不一样了,完全是用逻辑和通俗的语言来讲哲学,感觉就像一个智者在陪你聊天一样。我看这本书也是因为这个作者,之前看了他写的另外两本书,一本是佛教说些什么,一本是讲中国史的,都写得很好,微信读书里面都有。强力推荐。

  • 2019-12-15-学习c最少必要知识

    学习c最少必要知识

    C – Program Structure

    A C program basically consists of the following parts −

    • Preprocessor Commands
    • Functions
    • Variables
    • Statements & Expressions
    • Comments

    C – Basic Syntax

    A C program consists of various tokens and a token is either a keyword, an identifier, a constant, a string literal, or a symbol.

    In a C program, the semicolon is a statement terminator. That is, each individual statement must be ended with a semicolon. It indicates the end of one logical entity.

    Comments are like helping text in your C program and they are ignored by the compiler. They start with /* and terminate with the characters */ or start with //.

    A C identifier is a name used to identify a variable, function, or any other user-defined item. An identifier starts with a letter A to Z, a to z, or an underscore ‘_’ followed by zero or more letters, underscores, and digits (0 to 9). C is a case-sensitive programming language.

    The following list shows the reserved words in C. These reserved words may not be used as constants or variables or any other identifier names.

    auto else long switch break enum register typedef case extern return union char float short unsigned const for signed void continue goto sizeof volatile default if static while do int struct _Packed double

    A line containing only whitespace, possibly with a comment, is known as a blank line, and a C compiler totally ignores it.

    Whitespace is the term used in C to describe blanks, tabs, newline characters and comments. Whitespace separates one part of a statement from another and enables the compiler to identify where one element in a statement, such as int, ends and the next element begins.

    C – Data Types

    Data types in c refer to an extensive system used for declaring variables or functions of different types. The type of a variable determines how much space it occupies in storage and how the bit pattern stored is interpreted.

    The types in C can be classified as follows:

    • Basic Types: They are arithmetic types and are further classified into: (a) integer types and (b) floating-point types.
    • Enumerated types: They are again arithmetic types and they are used to define variables that can only assign certain discrete integer values throughout the program.
    • The type void: The type specifier void indicates that no value is available.
    • Derived types: They include (a) Pointer types, (b) Array types, (c) Structure types, (d) Union types and (e) Function types.

    Integer Types

    TypeStorage sizeValue range
    char1 byte-128 to 127 or 0 to 255
    unsigned char1 byte0 to 255
    signed char1 byte-128 to 127
    int2 or 4 bytes-32,768 to 32,767 or -2,147,483,648 to 2,147,483,647
    unsigned int2 or 4 bytes0 to 65,535 or 0 to 4,294,967,295
    short2 bytes-32,768 to 32,767
    unsigned short2 bytes0 to 65,535
    long8 bytes-9223372036854775808 to 9223372036854775807
    unsigned long8 bytes0 to 18446744073709551615

    Floating-Point Types

    TypeStorage sizeValue rangePrecision
    float4 byte1.2E-38 to 3.4E+386 decimal places
    double8 byte2.3E-308 to 1.7E+30815 decimal places
    long double10 byte3.4E-4932 to 1.1E+493219 decimal places

    The void Type

    • Function returns as void : There are various functions in C which do not return any value or you can say they return void. A function with no return value has the return type as void. For example, void exit (int status);
    • Function arguments as void: There are various functions in C which do not accept any parameter. A function with no parameter can accept a void. For example, int rand(void);
    • Pointers to void: A pointer of type void * represents the address of an object, but not its type. For example, a memory allocation function void *malloc( size_t size ); returns a pointer to void which can be casted to any data type.

    C – Variables

    A variable is nothing but a name given to a storage area that our programs can manipulate. Each variable in C has a specific type, which determines the size and layout of the variable’s memory; the range of values that can be stored within that memory; and the set of operations that can be applied to the variable.

    There are the following basic variable types:

    • char: Typically a single octet(one byte). This is an integer type.
    • int: The most natural size of integer for the machine.
    • float: A single-precision floating point value.
    • double: A double-precision floating point value.
    • void: Represents the absence of type.

    A variable definition tells the compiler where and how much storage to create for the variable. A variable definition specifies a data type and contains a list of one or more variables of that type as follows − type variable_list;

    Here, type must be a valid C data type including char, w_char, int, float, double, bool, or any user-defined object; and variable_list may consist of one or more identifier names separated by commas. Some valid declarations are shown here −

    int    i, j, k;
    char   c, ch;
    float  f, salary;
    double d;

    Variables can be initialized (assigned an initial value) in their declaration. The initializer consists of an equal sign followed by a constant expression as follows − type variable_name = value; Some examples are −

    extern int d = 3, f = 5;    // declaration of d and f.
    int d = 3, f = 5;           // definition and initializing d and f.
    byte z = 22;                // definition and initializes z.
    char x = 'x';               // the variable x has the value 'x'.

    For definition without an initializer: variables with static storage duration are implicitly initialized with NULL (all bytes have the value 0); the initial value of all other variables are undefined.

    A variable declaration provides assurance to the compiler that there exists a variable with the given type and name so that the compiler can proceed for further compilation without requiring the complete detail about the variable. A variable definition has its meaning at the time of compilation only, the compiler needs actual variable definition at the time of linking the program.

    A variable declaration is useful when you are using multiple files and you define your variable in one of the files which will be available at the time of linking of the program. You will use the keyword extern to declare a variable at any place. Though you can declare a variable multiple times in your C program, it can be defined only once in a file, a function, or a block of code.

    There are two kinds of expressions in C −

    lvalue − Expressions that refer to a memory location are called “lvalue” expressions. An lvalue may appear as either the left-hand or right-hand side of an assignment.

    rvalue − The term rvalue refers to a data value that is stored at some address in memory. An rvalue is an expression that cannot have a value assigned to it which means an rvalue may appear on the right-hand side but not on the left-hand side of an assignment.

    Variables are lvalues and so they may appear on the left-hand side of an assignment. Numeric literals are rvalues and so they may not be assigned and cannot appear on the left-hand side. Take a look at the following valid and invalid statements −

    int g = 20; // valid statement
    10 = 20; // invalid statement; would generate compile-time error

    C – Constants and Literals

    Constants refer to fixed values that the program may not alter during its execution. These fixed values are also called literals.

    Constants can be of any of the basic data types like an integer constant, a floating constant, a character constant, or a string literal. There are enumeration constants as well.

    Constants are treated just like regular variables except that their values cannot be modified after their definition.

    An integer literal can be a decimal, octal, or hexadecimal constant. A prefix specifies the base or radix: 0x or 0X for hexadecimal, 0 for octal, and nothing for decimal.

    An integer literal can also have a suffix that is a combination of U and L, for unsigned and long, respectively. The suffix can be uppercase or lowercase and can be in any order.

    Here are some examples of integer literals −

    212         /* Legal */
    215u        /* Legal */
    0xFeeL      /* Legal */
    078         /* Illegal: 8 is not an octal digit */
    032UU       /* Illegal: cannot repeat a suffix */
    85         /* decimal */
    0213       /* octal */
    0x4b       /* hexadecimal */
    30         /* int */
    30u        /* unsigned int */
    30l        /* long */
    30ul       /* unsigned long */

    A floating-point literal has an integer part, a decimal point, a fractional part, and an exponent part. You can represent floating point literals either in decimal form or exponential form.

    While representing decimal form, you must include the decimal point, the exponent, or both; and while representing exponential form, you must include the integer part, the fractional part, or both. The signed exponent is introduced by e or E.

    Here are some examples of floating-point literals −

    3.14159       /* Legal */
    314159E-5L    /* Legal */
    510E          /* Illegal: incomplete exponent */
    210f          /* Illegal: no decimal or exponent */
    .e55          /* Illegal: missing integer or fraction */

    Character literals are enclosed in single quotes, e.g., ‘x’ can be stored in a simple variable of char type.

    A character literal can be a plain character (e.g., ‘x’), an escape sequence (e.g., ‘\t’), or a universal character (e.g., ‘\u02C0’).

    There are certain characters in C that represent special meaning when preceded by a backslash for example, newline (\n) or tab (\t).

    Escape sequenceMeaning
    \|\ character
    \’‘ character
    \”” character
    \?? character
    \aAlert or bell
    \bBackspace
    \fForm feed
    \nNewline
    \rCarriage return
    \tHorizontal tab
    \vVertical tab
    \oooOctal number of one to three digits
    \xhh . . .Hexadecimal number of one or more digits

    String literals or constants are enclosed in double quotes “”. A string contains characters that are similar to character literals: plain characters, escape sequences, and universal characters.

    You can break a long line into multiple lines using string literals and separating them using white spaces.

    There are two simple ways in C to define constants −

    • Using #define preprocessor.
    • Using const keyword.

    Given below is the form to use #define preprocessor to define a constant − #define identifier value

    You can use const prefix to declare constants with a specific type as follows −const type variable = value;

    Note that it is a good programming practice to define constants in CAPITALS.

    C – Storage Classes

    A storage class defines the scope (visibility) and life-time of variables and/or functions within a C Program. They precede the type that they modify. We have four different storage classes in a C program −

    • auto
    • register
    • static
    • extern

    The auto storage class is the default storage class for all local variables.

    The register storage class is used to define local variables that should be stored in a register instead of RAM. This means that the variable has a maximum size equal to the register size (usually one word) and can’t have the unary ‘&’ operator applied to it (as it does not have a memory location).

    The register should only be used for variables that require quick access such as counters. It should also be noted that defining ‘register’ does not mean that the variable will be stored in a register. It means that it MIGHT be stored in a register depending on hardware and implementation restrictions.

    The static storage class instructs the compiler to keep a local variable in existence during the life-time of the program instead of creating and destroying it each time it comes into and goes out of scope. Therefore, making local variables static allows them to maintain their values between function calls.

    The static modifier may also be applied to global variables. When this is done, it causes that variable’s scope to be restricted to the file in which it is declared.

    In C programming, when static is used on a global variable, it causes only one copy of that member to be shared by all the objects of its class.

    The extern storage class is used to give a reference of a global variable that is visible to ALL the program files. When you use ‘extern’, the variable cannot be initialized however, it points the variable name at a storage location that has been previously defined.

    When you have multiple files and you define a global variable or function, which will also be used in other files, then extern will be used in another file to provide the reference of defined variable or function. Just for understanding, extern is used to declare a global variable or function in another file.

    The extern modifier is most commonly used when there are two or more files sharing the same global variables or functions as explained below.

    First File: main.c

    #include <stdio.h>
    int count ;
    extern void write_extern();
    main() {
       count = 5;
       write_extern();
    }

    Second File: support.c

    #include <stdio.h>
    extern int count;
    void write_extern(void) {
       printf("count is %d\n", count);
    }

    Here, extern is being used to declare count in the second file, where as it has its definition in the first file, main.c. Now, compile these two files as follows − $gcc main.c support.c

    It will produce the executable program a.out. When this program is executed, it produces the following result − count is 5

    C – Operators

    An operator is a symbol that tells the compiler to perform specific mathematical or logical functions. C language is rich in built-in operators and provides the following types of operators −

    • Arithmetic Operators
    • Relational Operators
    • Logical Operators
    • Bitwise Operators
    • Assignment Operators
    • Misc Operators

    Arithmetic Operators

    Assume variable A holds 10 and variable B holds 20 then −

    OperatorDescriptionExample
    +Adds two operands.A + B = 30
    Subtracts second operand from the first.A − B = -10
    *Multiplies both operands.A * B = 200
    /Divides numerator by de-numerator.B / A = 2
    %Modulus Operator and remainder of after an integer division.B % A = 0
    ++Increment operator increases the integer value by one.A++ = 11
    Decrement operator decreases the integer value by one.A– = 9

    Relational Operators

    Assume variable A holds 10 and variable B holds 20 then −

    OperatorDescriptionExample
    ==Checks if the values of two operands are equal or not. If yes, then the condition becomes true.(A == B) is not true.
    !=Checks if the values of two operands are equal or not. If the values are not equal, then the condition becomes true.(A != B) is true.
    >Checks if the value of left operand is greater than the value of right operand. If yes, then the condition becomes true.(A > B) is not true.
    <Checks if the value of left operand is less than the value of right operand. If yes, then the condition becomes true.(A < B) is true.
    >=Checks if the value of left operand is greater than or equal to the value of right operand. If yes, then the condition becomes true.(A >= B) is not true.
    <=Checks if the value of left operand is less than or equal to the value of right operand. If yes, then the condition becomes true.(A <= B) is true.

    Logical Operators

    Assume variable A holds 1 and variable B holds 0, then −

    OperatorDescriptionExample
    &&Called Logical AND operator. If both the operands are non-zero, then the condition becomes true.(A && B) is false.
    ||Called Logical OR Operator. If any of the two operands is non-zero, then the condition becomes true.(A || B) is true.
    !Called Logical NOT Operator. It is used to reverse the logical state of its operand. If a condition is true, then Logical NOT operator will make it false.!(A && B) is true.

    Bitwise Operators

    pqp & qp | qp ^ q
    00000
    01011
    11110
    10011

    Assume A = 60 and B = 13 in binary format, they will be as follows −

    A = 0011 1100
    B = 0000 1101
    A&B = 0000 1100
    A|B = 0011 1101
    A^B = 0011 0001
    ~A = 1100 0011

    The following table lists the bitwise operators supported by C. Assume variable ‘A’ holds 60 and variable ‘B’ holds 13, then −

    OperatorDescriptionExample
    &Binary AND Operator copies a bit to the result if it exists in both operands.(A & B) = 12, i.e., 0000 1100
    |Binary OR Operator copies a bit if it exists in either operand.(A | B) = 61, i.e., 0011 1101
    ^Binary XOR Operator copies the bit if it is set in one operand but not both.(A ^ B) = 49, i.e., 0011 0001
    ~Binary One’s Complement Operator is unary and has the effect of ‘flipping’ bits.(~A ) = ~(60), i.e,. -0111101
    <<Binary Left Shift Operator. The left operands value is moved left by the number of bits specified by the right operand.A << 2 = 240 i.e., 1111 0000
    >>Binary Right Shift Operator. The left operands value is moved right by the number of bits specified by the right operand.A >> 2 = 15 i.e., 0000 1111

    Assignment Operators

    OperatorDescriptionExample
    =Simple assignment operator. Assigns values from right side operands to left side operandC = A + B will assign the value of A + B to C
    +=Add AND assignment operator. It adds the right operand to the left operand and assign the result to the left operand.C += A is equivalent to C = C + A
    -=Subtract AND assignment operator. It subtracts the right operand from the left operand and assigns the result to the left operand.C -= A is equivalent to C = C – A
    *=Multiply AND assignment operator. It multiplies the right operand with the left operand and assigns the result to the left operand.C *= A is equivalent to C = C * A
    /=Divide AND assignment operator. It divides the left operand with the right operand and assigns the result to the left operand.C /= A is equivalent to C = C / A
    %=Modulus AND assignment operator. It takes modulus using two operands and assigns the result to the left operand.C %= A is equivalent to C = C % A
    <<=Left shift AND assignment operator.C <<= 2 is same as C = C << 2
    >>=Right shift AND assignment operator.C >>= 2 is same as C = C >> 2
    &=Bitwise AND assignment operator.C &= 2 is same as C = C & 2
    ^=Bitwise exclusive OR and assignment operator.C ^= 2 is same as C = C ^ 2
    |=Bitwise inclusive OR and assignment operator.C |= 2 is same as C = C | 2

    Operators Precedence in C

    Operator precedence determines the grouping of terms in an expression and decides how an expression is evaluated. Certain operators have higher precedence than others; for example, the multiplication operator has a higher precedence than the addition operator.

    Here, operators with the highest precedence appear at the top of the table, those with the lowest appear at the bottom. Within an expression, higher precedence operators will be evaluated first.

    CategoryOperatorAssociativity
    Postfix() [] -> . ++ —Left to right
    Unary+ – ! ~ ++ — (type)* & sizeofRight to left
    Multiplicative* / %Left to right
    Additive+ –Left to right
    Shift<< >>Left to right
    Relational< <= > >=Left to right
    Equality== !=Left to right
    Bitwise AND&Left to right
    Bitwise XOR^Left to right
    Bitwise OR|Left to right
    Logical AND&&Left to right
    Logical OR||Left to right
    Conditional?:Right to left
    Assignment= += -= *= /= %=>>= <<= &= ^= |=Right to left
    Comma,Left to right

    C – Decision Making

    Decision making structures require that the programmer specifies one or more conditions to be evaluated or tested by the program, along with a statement or statements to be executed if the condition is determined to be true, and optionally, other statements to be executed if the condition is determined to be false.

    C programming language assumes any non-zero and non-null values as true, and if it is either zero or null, then it is assumed as false value.

    C programming language provides the following types of decision making statements.

    Sr.No.Statement & Description
    1if statement
    An if statement consists of a boolean expression followed by one or more statements.
    2if…else statement
    An if statement can be followed by an optional else statement, which executes when the Boolean expression is false.
    3nested if statements
    You can use one if or else if statement inside another if or else if statement(s).
    4switch statement
    A switch statement allows a variable to be tested for equality against a list of values.
    5nested switch statements
    You can use one switch statement inside another switch statement(s).

    We have covered conditional operator ? : in the previous chapter which can be used to replace if…else statements. It has the following general form −

    Exp1 ? Exp2 : Exp3;

    Where Exp1, Exp2, and Exp3 are expressions. Notice the use and placement of the colon.

    The value of a ? expression is determined like this −

    • Exp1 is evaluated. If it is true, then Exp2 is evaluated and becomes the value of the entire ? expression.
    • If Exp1 is false, then Exp3 is evaluated and its value becomes the value of the expression.

    C – Loops

    A loop statement allows us to execute a statement or group of statements multiple times.

    C programming language provides the following types of loops to handle looping requirements.

    Sr.No.Loop Type & Description
    1while loop
    Repeats a statement or group of statements while a given condition is true. It tests the condition before executing the loop body.
    2for loop
    Executes a sequence of statements multiple times and abbreviates the code that manages the loop variable.
    3do…while loop
    It is more like a while statement, except that it tests the condition at the end of the loop body.
    4nested loops
    You can use one or more loops inside any other while, for, or do..while loop.

    Loop control statements change execution from its normal sequence. When execution leaves a scope, all automatic objects that were created in that scope are destroyed.

    C supports the following control statements.

    Sr.No.Control Statement & Description
    1break statement
    Terminates the loop or switch statement and transfers execution to the statement immediately following the loop or switch.
    2continue statement
    Causes the loop to skip the remainder of its body and immediately retest its condition prior to reiterating.
    3goto statement
    Transfers control to the labeled statement.

    A loop becomes an infinite loop if a condition never becomes false. The for loop is traditionally used for this purpose. Since none of the three expressions that form the ‘for’ loop are required, you can make an endless loop by leaving the conditional expression empty.

    C – Functions

    A function is a group of statements that together perform a task. Every C program has at least one function, which is main(), and all the most trivial programs can define additional functions.

    A function declaration tells the compiler about a function’s name, return type, and parameters. A function definition provides the actual body of the function.

    The C standard library provides numerous built-in functions that your program can call. For example, strcat() to concatenate two strings, memcpy() to copy one memory location to another location, and many more functions.

    A function can also be referred as a method or a sub-routine or a procedure, etc.

    The general form of a function definition in C programming language is as follows −

    return_type function_name( parameter list ) {
       body of the function
    }

    A function definition in C programming consists of a function header and a function body. Here are all the parts of a function −

    • Return Type − A function may return a value. The return_type is the data type of the value the function returns. Some functions perform the desired operations without returning a value. In this case, the return_type is the keyword void.
    • Function Name − This is the actual name of the function. The function name and the parameter list together constitute the function signature.
    • Parameters − A parameter is like a placeholder. When a function is invoked, you pass a value to the parameter. This value is referred to as actual parameter or argument. The parameter list refers to the type, order, and number of the parameters of a function. Parameters are optional; that is, a function may contain no parameters.
    • Function Body − The function body contains a collection of statements that define what the function does.

    A function declaration tells the compiler about a function name and how to call the function. The actual body of the function can be defined separately.

    A function declaration has the following parts − return_type function_name( parameter list );

    Parameter names are not important in function declaration only their type is required.

    While creating a C function, you give a definition of what the function has to do. To use a function, you will have to call that function to perform the defined task.

    When a program calls a function, the program control is transferred to the called function. A called function performs a defined task and when its return statement is executed or when its function-ending closing brace is reached, it returns the program control back to the main program.

    To call a function, you simply need to pass the required parameters along with the function name, and if the function returns a value, then you can store the returned value.

    Like:

    #include <stdio.h>
    int max(int a, int b) { return (a > b) ? a : b; }
    int main() {
        int a = 100, b = 200;
        printf("Max value is : %d\n", max(a, b));
        return 0;
    }

    If a function is to use arguments, it must declare variables that accept the values of the arguments. These variables are called the formal parameters of the function.

    Formal parameters behave like other local variables inside the function and are created upon entry into the function and destroyed upon exit.

    While calling a function, there are two ways in which arguments can be passed to a function −

    • Call by value: This method copies the actual value of an argument into the formal parameter of the function. In this case, changes made to the parameter inside the function have no effect on the argument.
    • Call by reference: This method copies the address of an argument into the formal parameter. Inside the function, the address is used to access the actual argument used in the call. This means that changes made to the parameter affect the argument.

    By default, C uses call by value to pass arguments. In general, it means the code within a function cannot alter the arguments used to call the function.

    C – Scope Rules

    A scope in any programming is a region of the program where a defined variable can have its existence and beyond that variable it cannot be accessed. There are three places where variables can be declared in C programming language −

    • Inside a function or a block which is called local variables.
    • Outside of all functions which is called global variables.
    • In the definition of function parameters which are called formal parameters.

    Variables that are declared inside a function or block are called local variables. They can be used only by statements that are inside that function or block of code. Local variables are not known to functions outside their own.

    Global variables are defined outside a function, usually on top of the program. Global variables hold their values throughout the lifetime of your program and they can be accessed inside any of the functions defined for the program. A global variable can be accessed by any function. That is, a global variable is available for use throughout your entire program after its declaration.

    A program can have same name for local and global variables but the value of local variable inside a function will take preference.

    Formal parameters, are treated as local variables with-in a function and they take precedence over global variables.

    When a local variable is defined, it is not initialized by the system, you must initialize it yourself. Global variables are initialized automatically by the system when you define them as follows −

    Data TypeInitial Default Value
    int0
    char‘\0’
    float0
    double0
    pointerNULL

    C – Arrays

    Arrays a kind of data structure that can store a fixed-size sequential collection of elements of the same type. An array is used to store a collection of data, but it is often more useful to think of an array as a collection of variables of the same type.

    Instead of declaring individual variables, such as number0, number1, …, and number99, you declare one array variable such as numbers and use numbers[0], numbers[1], and …, numbers[99] to represent individual variables. A specific element in an array is accessed by an index.

    All arrays consist of contiguous memory locations. The lowest address corresponds to the first element and the highest address to the last element.

    To declare an array in C, a programmer specifies the type of the elements and the number of elements required by an array as follows −
    type arrayName [ arraySize ];

    This is called a single-dimensional array. The arraySize must be an integer constant greater than zero and type can be any valid C data type, such as – double balance[10];

    You can initialize an array in C either one by one or using a single statement as follows −
    double balance[5] = {1000.0, 2.0, 3.4, 7.0, 50.0};

    The number of values between braces { } cannot be larger than the number of elements that we declare for the array between square brackets [ ].

    If you omit the size of the array, an array just big enough to hold the initialization is created. Therefore, if you write −
    double balance[] = {1000.0, 2.0, 3.4, 7.0, 50.0};

    You will create exactly the same array as you did in the previous example.

    The above statement assigns the 5th element in the array with a value of 50.0. All arrays have 0 as the index of their first element which is also called the base index and the last index of an array will be total size of the array minus 1.

    An element is accessed by indexing the array name. This is done by placing the index of the element within square brackets after the name of the array. For example −
    double salary = balance[9];

    The above statement will take the 10th element from the array and assign the value to salary variable.

    The following important concepts related to array should be clear to a C programmer −

    1. Multi-dimensional arrays: C supports multidimensional arrays. The simplest form of the multidimensional array is the two-dimensional array.
    2. Passing arrays to functions: You can pass to the function a pointer to an array by specifying the array’s name without an index.
    3. Return array from a function: C allows a function to return an array.
    4. Pointer to an array: You can generate a pointer to the first element of an array by simply specifying the array name, without any index.

    C – Pointers

    A pointer is a variable whose value is the address of another variable, i.e., direct address of the memory location. Like any variable or constant, you must declare a pointer before using it to store any variable address. The general form of a pointer variable declaration is −
    type *var-name;

    Here, type is the pointer’s base type; it must be a valid C data type and var-name is the name of the pointer variable. The asterisk * used to declare a pointer is the same asterisk used for multiplication. However, in this statement the asterisk is being used to designate a variable as a pointer. Take a look at some of the valid pointer declarations −

    int    *ip;    /* pointer to an integer */
    double *dp;    /* pointer to a double */
    float  *fp;    /* pointer to a float */
    char   *ch     /* pointer to a character */

    The actual data type of the value of all pointers, whether integer, float, character, or otherwise, is the same, a long hexadecimal number that represents a memory address. The only difference between pointers of different data types is the data type of the variable or constant that the pointer points to.

    There are a few important operations, which we will do with the help of pointers very frequently. (a) We define a pointer variable, (b) assign the address of a variable to a pointer and (c) finally access the value at the address available in the pointer variable. This is done by using unary operator * that returns the value of the variable located at the address specified by its operand.

    #include <stdio.h>
    int main() {
        int var = 20; /* actual variable declaration */
        int *ip;      /* pointer variable declaration */
        ip = &var; /* store address of var in pointer variable*/
        printf("Address of var variable: %p\n", &var);
        /* address stored in pointer variable */
        printf("Address stored in ip variable: %p\n", ip);
        /* access the value using the pointer */
        printf("Value of *ip variable: %d\n", *ip);
        return 0;
    }

    It is always a good practice to assign a NULL value to a pointer variable in case you do not have an exact address to be assigned. This is done at the time of variable declaration. A pointer that is assigned NULL is called a null pointer.

    The NULL pointer is a constant with a value of zero defined in several standard libraries.

    Pointers have many but easy concepts and they are very important to C programming. The following important pointer concepts should be clear to any C programmer −

    1. Pointer arithmetic: There are four arithmetic operators that can be used in pointers: ++, –, +, –
    2. Array of pointers: You can define arrays to hold a number of pointers.
    3. Pointer to pointer: C allows you to have pointer on a pointer and so on.
    4. Passing pointers to functions in C: Passing an argument by reference or by address enable the passed argument to be changed in the calling function by the called function.
    5. Return pointer from functions in C: C allows a function to return a pointer to the local variable, static variable, and dynamically allocated memory as well.

    C – Strings

    Strings are actually one-dimensional array of characters terminated by a null character ‘\0’. Thus a null-terminated string contains the characters that comprise the string followed by a null.

    The following declaration and initialization create a string consisting of the word “Hello”. To hold the null character at the end of the array, the size of the character array containing the string is one more than the number of characters in the word “Hello.”

    char greeting[6] = {'H', 'e', 'l', 'l', 'o', '\0'};

    Notice that char s[100]="hello"; printf("%d", strlen(s); will output 5 instead of 100;

    If you follow the rule of array initialization then you can write the above statement as follows −
    char greeting[] = "Hello";

    The C compiler automatically places the ‘\0’ at the end of the string when it initializes the array.

    C supports a wide range of functions that manipulate null-terminated strings −

    1. strcpy(s1, s2); Copies string s2 into string s1.
    2. strcat(s1, s2); Concatenates string s2 onto the end of string s1.
    3. strlen(s1); Returns the length of string s1.
    4. strcmp(s1, s2); Returns 0 if s1 and s2 are the same; less than 0 if s1s2.
    5. strchr(s1, ch); Returns a pointer to the first occurrence of character ch in string s1.
    6. strstr(s1, s2); Returns a pointer to the first occurrence of string s2 in string s1.

    C – Structures

    Arrays allow to define type of variables that can hold several data items of the same kind. Similarly structure is another user defined data type available in C that allows to combine data items of different kinds. Structures are used to represent a record.

    To define a structure, you must use the struct statement. The struct statement defines a new data type, with more than one member. The format of the struct statement is as follows −

    struct [structure tag] {
       member definition;
       member definition;
       ...
       member definition;
    } [one or more structure variables];

    struct without a tag has an “anonymous structure type” and you won’t be able to declare another variable of the same type.

    To access any member of a structure, we use the member access operator (.). The member access operator is coded as a period between the structure variable name and the structure member that we wish to access. You would use the keyword struct to define variables of structure type.

    #include <stdio.h>
    #include <string.h>
    struct Books {
        char title[50];
        char author[50];
        char subject[100];
        int book_id;
    };
    int main() {
        struct Books book; /* Declare book of type Book */
        /* book 1 specification */
        // book.title = "C Programming"; // will cause error: array type 'char [50]' is not assignable
        strcpy(book.title, "C Programming");
        strcpy(book.author, "Nuha Ali");
        strcpy(book.subject, "C Programming Tutorial");
        book.book_id = 6495407;
        /* print book info */
        printf("Book title : %s\n", book.title);
        printf("Book author : %s\n", book.author);
        printf("Book subject : %s\n", book.subject);
        printf("Book book_id : %d\n", book.book_id);
        return 0;
    }

    You can pass a structure as a function argument in the same way as you pass any other variable or pointer.

    You can define pointers to structures in the same way as you define pointer to any other variable −
    struct Books *pb = &book;

    To find the address of a structure variable, place the ‘&’; operator before the structure’s name.

    To access the members of a structure using a pointer to that structure, you must use the → operator as follows − pb->id

    #include <stdio.h>
    #include <string.h>
    struct Books {
        char title[50];
        char author[50];
        char subject[100];
        int book_id;
    };
    void printBook(struct Books);
    void updateBookId(struct Books *, int);
    int main() {
        struct Books book, *pb = &book;
        strcpy(book.title, "C Programming");
        strcpy(book.author, "Nuha Ali");
        strcpy(book.subject, "C Programming Tutorial");
        book.book_id = 6495407;
        printBook(book);
        updateBookId(pb, 100);
        strcpy(pb->author, "Alan");
        printBook(book);
        return 0;
    }
    void printBook(struct Books book) {
        printf("Book title : %s\n", book.title);
        printf("Book author : %s\n", book.author);
        printf("Book subject : %s\n", book.subject);
        printf("Book book_id : %d\n", book.book_id);
    }
    void updateBookId(struct Books *book, int id) { book->book_id = id; }

    Bit Fields

    Bit Fields allow the packing of data in a structure. This is especially useful when memory or data storage is at a premium. Typical examples include −

    Packing several objects into a machine word. e.g. 1 bit flags can be compacted.

    Reading external file formats — non-standard file formats could be read in, e.g., 9-bit integers.

    C allows us to do this in a structure definition by putting :bit length after the variable. For example −

    struct packed_struct {
       unsigned int f1:1;
       unsigned int f2:1;
       unsigned int f3:1;
       unsigned int f4:1;
       unsigned int type:4;
       unsigned int my_int:9;
    } pack;

    Here, the packed_struct contains 6 members: Four 1 bit flags f1..f3, a 4-bit type and a 9-bit my_int.

    C automatically packs the above bit fields as compactly as possible, provided that the maximum length of the field is less than or equal to the integer word length of the computer. If this is not the case, then some compilers may allow memory overlap for the fields while others would store the next field in the next word.

    C – Unions

    A union is a special data type available in C that allows to store different data types in the same memory location. You can define a union with many members, but only one member can contain a value at any given time. Unions provide an efficient way of using the same memory location for multiple-purpose.

    To define a union, you must use the union statement in the same way as you did while defining a structure. The union statement defines a new data type with more than one member for your program. The format of the union statement is as follows −

    union [union tag] {
       member definition;
       member definition;
       ...
       member definition;
    } [one or more union variables];

    The union tag is optional and each member definition is a normal variable definition, such as int i; or float f; or any other valid variable definition. At the end of the union’s definition, before the final semicolon, you can specify one or more union variables but it is optional. Here is the way you would define a union type named Data having three members i, f, and str −

    union Data {
       int i;
       float f;
       char str[20];
    } data;

    Now, a variable of Data type can store an integer, a floating-point number, or a string of characters. It means a single variable, i.e., same memory location, can be used to store multiple types of data. You can use any built-in or user defined data types inside a union based on your requirement.

    The memory occupied by a union will be large enough to hold the largest member of the union. For example, in the above example, Data type will occupy 20 bytes of memory space because this is the maximum space which can be occupied by a character string. The following example displays the total memory size occupied by the above union −

    #include <stdio.h>
    #include <string.h>
    union Data {
        int i;
        float f;
        char str[20];
    };
    int main() {
        union Data data;
        printf("Memory size occupied by data : %lu\n", sizeof(data)); //20
        return 0;
    }

    To access any member of a union, we use the member access operator (.). The member access operator is coded as a period between the union variable name and the union member that we wish to access. You would use the keyword union to define variables of union type. The following example shows how to use unions in a program −

    #include <stdio.h>
    #include <string.h>
    union Data {
        int i;
        float f;
        char str[20];
    };
    int main() {
        union Data data;
        data.i = 10;
        data.f = 220.5;
        strcpy(data.str, "C Programming");
        printf("data.i : %d\n", data.i);
        printf("data.f : %f\n", data.f);
        printf("data.str : %s\n", data.str);
        return 0;
    }

    output:

    data.i : 1917853763
    data.f : 4122360580327794860452759994368.000000
    data.str : C Programming

    Here, we can see that the values of i and f members of union got corrupted because the final value assigned to the variable has occupied the memory location and this is the reason that the value of str member is getting printed very well.

    Now let’s look into the same example once again where we will use one variable at a time which is the main purpose of having unions −

    #include <stdio.h>
    #include <string.h>
    union Data {
        int i;
        float f;
        char str[20];
    };
    int main() {
        union Data data;
        data.i = 10;
        printf("data.i : %d\n", data.i);
        data.f = 220.5;
        printf("data.f : %f\n", data.f);
        strcpy(data.str, "C Programming");
        printf("data.str : %s\n", data.str);
        return 0;
    }

    output:

    data.i : 10
    data.f : 220.500000
    data.str : C Programming

    C – Bit Fields

    struct {
       unsigned int widthValidated;
       unsigned int heightValidated;
    } status;

    can be rewritten as:

    struct {
       unsigned int widthValidated : 1;
       unsigned int heightValidated : 1;
    } status;

    The above structure requires 4 bytes of memory space for status variable, but only 2 bits will be used to store the values.

    If you will use up to 32 variables each one with a width of 1 bit, then also the status structure will use 4 bytes. However as soon as you have 33 variables, it will allocate the next slot of the memory and it will start using 8 bytes. Let us check the following example to understand the concept −

    The declaration of a bit-field has the following form inside a structure −

    struct {
       type [member_name] : width ;
    };

    type: An integer type that determines how a bit-field’s value is interpreted. The type may be int, signed int, or unsigned int.

    member_name: The name of the bit-field.

    width: The number of bits in the bit-field. The width must be less than or equal to the bit width of the specified type.

    The variables defined with a predefined width are called bit fields. A bit field can hold more than a single bit; for example, if you need a variable to store a value from 0 to 7, then you can define a bit field with a width of 3 bits as follows −

    struct {
       unsigned int age : 3;
    } Age;

    The above structure definition instructs the C compiler that the age variable is going to use only 3 bits to store the value. If you try to use more than 3 bits, then it will not allow you to do so. Let us try the following example −

    #include <stdio.h>
    #include <string.h>
    struct {
        unsigned int age : 3;
    } Age;
    int main() {
        Age.age = 4;
        printf("Sizeof( Age ) : %lu\n", sizeof(Age));
        printf("Age.age : %d\n", Age.age);
        Age.age = 7;
        printf("Age.age : %d\n", Age.age);
        Age.age = 8;
        printf("Age.age : %d\n", Age.age); // output 0 with warning
        return 0;
    }

    C – typedef

    The C programming language provides a keyword called typedef, which you can use to give a type a new name. Following is an example to define a term BYTE for one-byte numbers −

    typedef unsigned char BYTE;

    After this type definition, the identifier BYTE can be used as an abbreviation for the type unsigned char, for example..

    BYTE b1, b2;

    By convention, uppercase letters are used for these definitions to remind the user that the type name is really a symbolic abbreviation, but you can use lowercase, as follows −

    typedef unsigned char byte;

    You can use typedef to give a name to your user defined data types as well. For example,

    typedef struct Books {
       char title[50];
       char author[50];
       char subject[100];
       int book_id;
    } Book;

    define is a C-directive which is also used to define the aliases for various data types similar to typedef but with the following differences −

    typedef is limited to giving symbolic names to types only where as #define can be used to define alias for values as well, you can define 1 as ONE etc.

    typedef interpretation is performed by the compiler whereas #define statements are processed by the pre-processor.

    C – Input and Output

    When we say Input, it means to feed some data into a program. An input can be given in the form of a file or from the command line. C programming provides a set of built-in functions to read the given input and feed it to the program as per requirement.

    When we say Output, it means to display some data on screen, printer, or in any file. C programming provides a set of built-in functions to output the data on the computer screen as well as to save it in text or binary files.

    C programming treats all the devices as files. So devices such as the display are addressed in the same way as files and the following three files are automatically opened when a program executes to provide access to the keyboard and screen.

    Standard FileFile PointerDevice
    Standard inputstdinKeyboard
    Standard outputstdoutScreen
    Standard errorstderrYour screen

    The file pointers are the means to access the file for reading and writing purpose. This section explains how to read values from the screen and how to print the result on the screen.

    The int getchar(void) function reads the next available character from the screen and returns it as an integer. This function reads only single character at a time. You can use this method in the loop in case you want to read more than one character from the screen.

    The int putchar(int c) function puts the passed character on the screen and returns the same character. This function puts only single character at a time. You can use this method in the loop in case you want to display more than one character on the screen. Check the following example −

    #include <stdio.h>
    int main( ) {
       int c;
       printf( "Enter a value :");
       c = getchar( );
       printf( "\nYou entered: ");
       putchar( c );
       return 0;
    }

    The char *gets(char *s) function reads a line from stdin into the buffer pointed to by s until either a terminating newline or EOF (End of File).

    The int puts(const char *s) function writes the string ‘s’ and ‘a’ trailing newline to stdout.

    NOTE: Though it has been deprecated to use gets() function, Instead of using gets, you want to use fgets().

    #include <stdio.h>
    int main() {
        char str[100];
        printf("Enter a value :");
        // gets(str); warning: this program uses gets(), which is unsafe.
        fgets(str, 100, stdin);
        printf("You entered: ");
        puts(str);
        return 0;
    }

    The int scanf(const char *format, …) function reads the input from the standard input stream stdin and scans that input according to the format provided.

    The int printf(const char *format, …) function writes the output to the standard output stream stdout and produces the output according to the format provided.

    The format can be a simple constant string, but you can specify %s, %d, %c, %f, etc., to print or read strings, integer, character or float respectively. There are many other formatting options available which can be used based on requirements. Let us now proceed with a simple example to understand the concepts better −

    Here, it should be noted that scanf() expects input in the same format as you provided %s and %d, which means you have to provide valid inputs like “string integer”. If you provide “string string” or “integer integer”, then it will be assumed as wrong input. Secondly, while reading a string, scanf() stops reading as soon as it encounters a space, so “this is test” are three strings for scanf().

    C – File I/O

    A file represents a sequence of bytes, regardless of it being a text file or a binary file. C programming language provides access on high level functions as well as low level (OS level) calls to handle file on your storage devices.

    You can use the fopen( ) function to create a new file or to open an existing file. This call will initialize an object of the type FILE, which contains all the information necessary to control the stream. The prototype of this function call is as follows −

    FILE *fopen( const char * filename, const char * mode );

    Here, filename is a string literal, which you will use to name your file, and access mode can have one of the following values −

    1. r: Opens an existing text file for reading purpose.
    2. w: Opens a text file for writing. If it does not exist, then a new file is created. Here your program will start writing content from the beginning of the file.
    3. a: Opens a text file for writing in appending mode. If it does not exist, then a new file is created. Here your program will start appending content in the existing file content.
    4. r+: Opens a text file for both reading and writing.
    5. w+: Opens a text file for both reading and writing. It first truncates the file to zero length if it exists, otherwise creates a file if it does not exist.
    6. a+: Opens a text file for both reading and writing. It creates the file if it does not exist. The reading will start from the beginning but writing can only be appended.

    If you are going to handle binary files, then you will use following access modes instead of the above mentioned ones −

    "rb", "wb", "ab", "rb+", "r+b", "wb+", "w+b", "ab+", "a+b"

    To close a file, use the fclose( ) function. The prototype of this function is −

    int fclose( FILE *fp );

    The fclose(-) function returns zero on success, or EOF if there is an error in closing the file. This function actually flushes any data still pending in the buffer to the file, closes the file, and releases any memory used for the file. The EOF is a constant defined in the header file stdio.h.

    Following is the simplest function to write individual characters to a stream −

    int fputc( int c, FILE *fp );

    The function fputc() writes the character value of the argument c to the output stream referenced by fp. It returns the written character written on success otherwise EOF if there is an error. You can use the following functions to write a null-terminated string to a stream −

    int fputs( const char *s, FILE *fp );

    The function fputs() writes the string s to the output stream referenced by fp. It returns a non-negative value on success, otherwise EOF is returned in case of any error. You can use int fprintf(FILE *fp,const char *format, …) function as well to write a string into a file.

    #include <stdio.h>
    int main() {
       FILE *fp;
       fp = fopen("test.log", "w+");
       fprintf(fp, "This is testing for fprintf...\n");
       fputs("This is testing for fputs...\n", fp);
       fclose(fp);
    }

    Given below is the simplest function to read a single character from a file −

    int fgetc( FILE * fp );

    The fgetc() function reads a character from the input file referenced by fp. The return value is the character read, or in case of any error, it returns EOF. The following function allows to read a string from a stream −

    char *fgets( char *buf, int n, FILE *fp );

    The functions fgets() reads up to n-1 characters from the input stream referenced by fp. It copies the read string into the buffer buf, appending a null character to terminate the string.

    If this function encounters a newline character ‘\n’ or the end of the file EOF before they have read the maximum number of characters, then it returns only the characters read up to that point including the new line character. You can also use int fscanf(FILE *fp, const char *format, ...) function to read strings from a file, but it stops reading after encountering the first space character.

    There are two functions, that can be used for binary input and output −

    size_t fread(void *ptr, size_t size_of_elements, size_t number_of_elements, FILE *a_file);

    size_t fwrite(const void *ptr, size_t size_of_elements, size_t number_of_elements, FILE *a_file);

    Both of these functions should be used to read or write blocks of memories – usually arrays or structures.

    C – Preprocessors

    The C Preprocessor is not a part of the compiler, but is a separate step in the compilation process. In simple terms, a C Preprocessor is just a text substitution tool and it instructs the compiler to do required pre-processing before the actual compilation. We’ll refer to the C Preprocessor as CPP.

    All preprocessor commands begin with a hash symbol (#). It must be the first nonblank character, and for readability, a preprocessor directive should begin in the first column. The following section lists down all the important preprocessor directives −

    1. #define: Substitutes a preprocessor macro.
    2. #include: Inserts a particular header from another file.
    3. #undef: Undefines a preprocessor macro.
    4. #ifdef: Returns true if this macro is defined.
    5. #ifndef: Returns true if this macro is not defined.
    6. #if: Tests if a compile time condition is true.
    7. #else: The alternative for #if.
    8. #elif: #else and #if in one statement.
    9. #endif: Ends preprocessor conditional.
    10. #error: Prints error message on stderr.
    11. #pragma: Issues special commands to the compiler, using a standardized method.

    Examples:

    #define MAX_ARRAY_LENGTH 20

    This directive tells the CPP to replace instances of MAX_ARRAY_LENGTH with 20. Use #define for constants to increase readability.

    #include <stdio.h>
    #include "myheader.h"

    These directives tell the CPP to get stdio.h from System Libraries and add the text to the current source file. The next line tells CPP to get myheader.h from the local directory and add the content to the current source file.

    #undef  FILE_SIZE
    #define FILE_SIZE 42

    It tells the CPP to undefine existing FILE_SIZE and define it as 42.

    #ifndef MESSAGE
       #define MESSAGE "You wish!"
    #endif

    It tells the CPP to define MESSAGE only if MESSAGE isn’t already defined.

    #ifdef DEBUG
       /* Your debugging statements here */
    #endif

    It tells the CPP to process the statements enclosed if DEBUG is defined. This is useful if you pass the -DDEBUG flag to the gcc compiler at the time of compilation. This will define DEBUG, so you can turn debugging on and off on the fly during compilation.

    ANSI C defines a number of macros. Although each one is available for use in programming, the predefined macros should not be directly modified.

    1. DATE The current date as a character literal in “MMM DD YYYY” format.
    2. TIME The current time as a character literal in “HH:MM:SS” format.
    3. FILE This contains the current filename as a string literal.
    4. LINE This contains the current line number as a decimal constant.
    5. STDC Defined as 1 when the compiler complies with the ANSI standard.

    The C preprocessor offers the following operators to help create macros −

    The Macro Continuation () Operator

    A macro is normally confined to a single line. The macro continuation operator () is used to continue a macro that is too long for a single line. For example −

    #define  message_for(a, b)  \
       printf(#a " and " #b ": We love you!\n")

    The Stringize (#) Operator

    The stringize or number-sign operator ( ‘#’ ), when used within a macro definition, converts a macro parameter into a string constant. This operator may be used only in a macro having a specified argument or parameter list. For example −

    #include <stdio.h>
    #define  message_for(a, b)  \
           printf(#a " and " #b ": We love you!\n")
    int main(void) {
       message_for(Carole, Debra);
       return 0;
    }

    The Token Pasting (##) Operator

    The token-pasting operator (##) within a macro definition combines two arguments. It permits two separate tokens in the macro definition to be joined into a single token. For example −

    #include <stdio.h>
    #define tokenpaster(n) printf("token" #n " = %d\n", token##n)
    int main(void) {
        int token34 = 40;
        int token35 = 50;
        tokenpaster(34); // = printf ("token34 = %d", token34);
        tokenpaster(35);
        return 0;
    }

    The Defined() Operator

    The preprocessor defined operator is used in constant expressions to determine if an identifier is defined using #define. If the specified identifier is defined, the value is true (non-zero). If the symbol is not defined, the value is false (zero). The defined operator is specified as follows −

    #include <stdio.h>
    #if !defined(MESSAGE)
        #define MESSAGE "You wish!"
    #endif
    int main(void) {
        printf("Here is the message: %s\n", MESSAGE);
        return 0;
    }

    Parameterized Macros

    One of the powerful functions of the CPP is the ability to simulate functions using parameterized macros. For example, we might have some code to square a number as follows −

    int square(int x) {return x * x;}

    We can rewrite above the code using a macro as follows −

    #define square(x) ((x) * (x))

    Macros with arguments must be defined using the #define directive before they can be used. The argument list is enclosed in parentheses and must immediately follow the macro name. Spaces are not allowed between the macro name and open parenthesis. For example −

    #include <stdio.h>
    #define MAX(x, y) ((x) > (y) ? (x) : (y))
    int main(void) {
        printf("Max between 20 and 10 is %d\n", MAX(10, 20));
        return 0;
    }

    C – Header Files

    A header file is a file with extension .h which contains C function declarations and macro definitions to be shared between several source files. There are two types of header files: the files that the programmer writes and the files that comes with your compiler.

    You request to use a header file in your program by including it with the C preprocessing directive #include, like you have seen inclusion of stdio.h header file, which comes along with your compiler.

    Including a header file is equal to copying the content of the header file but we do not do it because it will be error-prone and it is not a good idea to copy the content of a header file in the source files, especially if we have multiple source files in a program.

    A simple practice in C or C++ programs is that we keep all the constants, macros, system wide global variables, and function prototypes in the header files and include that header file wherever it is required.

    Both the user and the system header files are included using the preprocessing directive #include. It has the following two forms −

    #include <file>

    This form is used for system header files. It searches for a file named ‘file’ in a standard list of system directories. You can prepend directories to this list with the -I option while compiling your source code.

    #include "file"

    This form is used for header files of your own program. It searches for a file named ‘file’ in the directory containing the current file. You can prepend directories to this list with the -I option while compiling your source code.

    The #include directive works by directing the C preprocessor to scan the specified file as input before continuing with the rest of the current source file. The output from the preprocessor contains the output already generated, followed by the output resulting from the included file, followed by the output that comes from the text after the #include directive. For example, if you have a header file header.h as follows −

    char *test (void);

    and a main program called program.c that uses the header file, like this −

    int x;
    #include "header.h"
    int main (void) {
       puts (test ());
    }

    the compiler will see the same token stream as it would if program.c read.

    int x;
    char *test (void);
    int main (void) {
       puts (test ());
    }

    If a header file happens to be included twice, the compiler will process its contents twice and it will result in an error. The standard way to prevent this is to enclose the entire real contents of the file in a conditional, like this −

    #ifndef HEADER_FILE
    #define HEADER_FILE
    the entire header file file
    #endif

    This construct is commonly known as a wrapper #ifndef. When the header is included again, the conditional will be false, because HEADER_FILE is defined. The preprocessor will skip over the entire contents of the file, and the compiler will not see it twice.

    Sometimes it is necessary to select one of the several different header files to be included into your program. For instance, they might specify configuration parameters to be used on different sorts of operating systems. You could do this with a series of conditionals as follows −

    #if SYSTEM_1
       # include "system_1.h"
    #elif SYSTEM_2
       # include "system_2.h"
    #elif SYSTEM_3
       ...
    #endif

    But as it grows, it becomes tedious, instead the preprocessor offers the ability to use a macro for the header name. This is called a computed include. Instead of writing a header name as the direct argument of #include, you simply put a macro name there −

    #define SYSTEM_H "system_1.h"
    ...
    #include SYSTEM_H

    SYSTEM_H will be expanded, and the preprocessor will look for system_1.h as if the #include had been written that way originally. SYSTEM_H could be defined by your Makefile with a -D option.

    C – Type Casting

    Type casting is a way to convert a variable from one data type to another data type. For example, if you want to store a ‘long’ value into a simple integer then you can type cast ‘long’ to ‘int’. You can convert the values from one type to another explicitly using the cast operator as follows −

    (type_name) expression

    Consider the following example where the cast operator causes the division of one integer variable by another to be performed as a floating-point operation −

    #include <stdio.h>
    main() {
       int sum = 17, count = 5;
       double mean;
       mean = (double) sum / count;
       printf("Value of mean : %f\n", mean ); // Value of mean : 3.400000
    }

    It should be noted here that the cast operator has precedence over division, so the value of sum is first converted to type double and finally it gets divided by count yielding a double value.

    Type conversions can be implicit which is performed by the compiler automatically, or it can be specified explicitly through the use of the cast operator. It is considered good programming practice to use the cast operator whenever type conversions are necessary.

    Integer promotion is the process by which values of integer type “smaller” than int or unsigned int are converted either to int or unsigned int. Consider an example of adding a character with an integer −

    #include <stdio.h>
    int main() {
        int i = 17;
        char c = 'c'; /* ascii value is 99 */
        int sum = i + c;
        printf("Value of sum : %d\n", sum); //Value of sum : 116
    }

    The usual arithmetic conversions are implicitly performed to cast their values to a common type. The compiler first performs integer promotion; if the operands still have different types, then they are converted to the type that appears highest in the following hierarchy −

    int->unsigned int->long->unsigned long->long long->unsigned long long->float->double->long double

    The usual arithmetic conversions are not performed for the assignment operators, nor for the logical operators && and ||. Let us take the following example to understand the concept −

    #include <stdio.h>
    int main() {
        int i = 17;
        char c = 'c'; /* ascii value is 99 */
        float sum = i + c;
        printf("Value of sum : %f\n", sum); // Value of sum : 116.000000
    }

    Here, it is simple to understand that first c gets converted to integer, but as the final value is double, usual arithmetic conversion applies and the compiler converts i and c into ‘float’ and adds them yielding a ‘float’ result.

    C – Error Handling

    As such, C programming does not provide direct support for error handling but being a system programming language, it provides you access at lower level in the form of return values. Most of the C or even Unix function calls return -1 or NULL in case of any error and set an error code errno. It is set as a global variable and indicates an error occurred during any function call. You can find various error codes defined in header file.

    So a C programmer can check the returned values and can take appropriate action depending on the return value. It is a good practice, to set errno to 0 at the time of initializing a program. A value of 0 indicates that there is no error in the program.

    The C programming language provides perror() and strerror() functions which can be used to display the text message associated with errno.

    The perror() function displays the string you pass to it, followed by a colon, a space, and then the textual representation of the current errno value.

    The strerror() function, which returns a pointer to the textual representation of the current errno value.

    Let’s try to simulate an error condition and try to open a file which does not exist. Here I’m using both the functions to show the usage, but you can use one or more ways of printing your errors. Second important point to note is that you should use stderr file stream to output all the errors.

    #include <errno.h>
    #include <stdio.h>
    #include <string.h>
    extern int errno;
    int main() {
        FILE *pf;
        int errnum;
        pf = fopen("unexist.txt", "rb");
        if (pf == NULL) {
            errnum = errno;
            fprintf(stderr, "Value of errno: %d\n", errno);
            perror("Error printed by perror");
            fprintf(stderr, "Error opening file: %s\n", strerror(errnum));
        } else {
            fclose(pf);
        }
        return 0;
    }

    output:

    Value of errno: 2
    Error printed by perror: No such file or directory
    Error opening file: No such file or directory

    It is a common problem that at the time of dividing any number, programmers do not check if a divisor is zero and finally it creates a runtime error.

    The code below fixes this by checking if the divisor is zero before dividing −

    #include <stdio.h>
    #include <stdlib.h>
    int main() {
        int dividend = 20;
        int divisor = 0;
        int quotient;
        if (divisor == 0) {
            fprintf(stderr, "Division by zero! Exiting...\n");
            exit(-1);
        }
        quotient = dividend / divisor;
        fprintf(stderr, "Value of quotient : %d\n", quotient);
        exit(0);
    }

    It is a common practice to exit with a value of EXIT_SUCCESS in case of program coming out after a successful operation. Here, EXIT_SUCCESS is a macro and it is defined as 0.

    If you have an error condition in your program and you are coming out then you should exit with a status EXIT_FAILURE which is defined as -1. So let’s write above program as follows −

    #include <stdio.h>
    #include <stdlib.h>
    int main() {
        int dividend = 20;
        int divisor = 5;
        int quotient;
        if (divisor == 0) {
            fprintf(stderr, "Division by zero! Exiting...\n");
            exit(EXIT_FAILURE);
        }
        quotient = dividend / divisor;
        fprintf(stderr, "Value of quotient : %d\n", quotient);
        exit(EXIT_SUCCESS);
    }

    C – Recursion

    Recursion is the process of repeating items in a self-similar way. In programming languages, if a program allows you to call a function inside the same function, then it is called a recursive call of the function.

    void recursion() {
       recursion(); /* function calls itself */
    }

    The C programming language supports recursion, i.e., a function to call itself. But while using recursion, programmers need to be careful to define an exit condition from the function, otherwise it will go into an infinite loop.

    Recursive functions are very useful to solve many mathematical problems, such as calculating the factorial of a number, generating Fibonacci series, etc.

    The following example calculates the factorial of a given number using a recursive function −

    #include <stdio.h>
    #define N 200
    long double fibonacci(unsigned int i) {
        static long double f[N];
        if (i <= 1) {
            f[i] = i;
        } else {
            if (f[i] == 0) {
                f[i]= fibonacci(i - 1) + fibonacci(i - 2);
            }
        }
        return f[i];
    }
    int main() {
        int i;
        for (i = 0; i < N; i++) {
            printf("%Lf\t\n", fibonacci(i));
        }
        return 0;
    }

    C – Variable Arguments

    Sometimes, you may come across a situation, when you want to have a function, which can take variable number of arguments, i.e., parameters, instead of predefined number of parameters. The C programming language provides a solution for this situation and you are allowed to define a function which can accept variable number of parameters based on your requirement. The following example shows the definition of such a function.

    int func(int, ... ) {
       .
    }
    int main() {
       func(1, 2, 3);
       func(1, 2, 3, 4);
    }

    It should be noted that the function func() has its last argument as ellipses, i.e. three dotes (…) and the one just before the ellipses is always an int which will represent the total number variable arguments passed. To use such functionality, you need to make use of stdarg.h header file which provides the functions and macros to implement the functionality of variable arguments and follow the given steps −

    • Define a function with its last parameter as ellipses and the one just before the ellipses is always an int which will represent the number of arguments.
    • Create a va_list type variable in the function definition. This type is defined in stdarg.h header file.
    • Use int parameter and va_start macro to initialize the va_list variable to an argument list. The macro va_start is defined in stdarg.h header file.
    • Use va_arg macro and va_list variable to access each item in argument list.
    • Use a macro va_end to clean up the memory assigned to va_list variable.

    Now let us follow the above steps and write down a simple function which can take the variable number of parameters and return their average −

    #include <stdarg.h>
    #include <stdio.h>
    double average(int num, ...) {
        va_list valist;
        double sum = 0.0;
        int i;
        /* initialize valist for num number of arguments */
        va_start(valist, num);
        /* access all the arguments assigned to valist */
        for (i = 0; i < num; i++) {
            sum += va_arg(valist, int);
        }
        /* clean memory reserved for valist */
        va_end(valist);
        return sum / num;
    }
    int main() {
        printf("Average of 2, 3, 4, 5 = %f\n", average(4, 2, 3, 4, 5));
        printf("Average of 5, 10, 15 = %f\n", average(3, 5, 10, 15));
    }

    C – Memory Management

    The C programming language provides several functions for memory allocation and management. These functions can be found in the header file.

    1. void *calloc(int num, int size); This function allocates an array of num elements each of which size in bytes will be size.
    2. void free(void *address); This function releases a block of memory block specified by address.
    3. void *malloc(int num); This function allocates an array of num bytes and leave them uninitialized.
    4. void *realloc(void *address, int newsize); This function re-allocates memory extending it upto newsize.

    While programming, if you are aware of the size of an array, then it is easy and you can define it as an array. For example, to store a name of any person, it can go up to a maximum of 100 characters, so you can define something as follows −

    char name[100];

    But now let us consider a situation where you have no idea about the length of the text you need to store, for example, you want to store a detailed description about a topic. Here we need to define a pointer to character without defining how much memory is required and later, based on requirement, we can allocate memory as shown in the below example −

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    int main() {
        char name[100];
        char *description;
        strcpy(name, "Zara Ali");
        /* allocate memory dynamically */
        // description = malloc(200 * sizeof(char));
        description = calloc(200, sizeof(char));
        if (description == NULL) {
            fprintf(stderr, "Error - unable to allocate required memory\n");
        } else {
            strcpy(description, "Zara ali a DPS student in class 10th");
        }
        printf("Name = %s\n", name);
        printf("Description: %s\n", description);
    }

    When your program comes out, operating system automatically release all the memory allocated by your program but as a good practice when you are not in need of memory anymore then you should release that memory by calling the function free().

    Alternatively, you can increase or decrease the size of an allocated memory block by calling the function realloc(). Let us check the above program once again and make use of realloc() and free() functions −

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    int main() {
        char name[100];
        char *description;
        strcpy(name, "Zara Ali");
        /* allocate memory dynamically */
        description = malloc(30 * sizeof(char));
        if (description == NULL) {
            fprintf(stderr, "Error - unable to allocate required memory\n");
        } else {
            strcpy(description, "Zara ali a DPS student.");
        }
        /* suppose you want to store bigger description */
        description = realloc(description, 100 * sizeof(char));
        if (description == NULL) {
            fprintf(stderr, "Error - unable to allocate required memory\n");
        } else {
            strcat(description, "She is in class 10th");
        }
        printf("Name = %s\n", name);
        printf("Description: %s\n", description);
        /* release memory using free() function */
        free(description);
        return 0;
    }

    C – Command Line Arguments

    It is possible to pass some values from the command line to your C programs when they are executed. These values are called command line arguments and many times they are important for your program especially when you want to control your program from outside instead of hard coding those values inside the code.

    The command line arguments are handled using main() function arguments where argc refers to the number of arguments passed, and argv[] is a pointer array which points to each argument passed to the program. Following is a simple example which checks if there is any argument supplied from the command line and take action accordingly −

    #include <stdio.h>
    int main(int argc, char *argv[]) {
        printf("Program name %s\n", argv[0]);
        if (argc == 2) {
            printf("The argument supplied is %s\n", argv[1]);
        } else if (argc > 2) {
            printf("Too many arguments supplied.\n");
        } else {
            printf("One argument expected.\n");
        }
    }

    It should be noted that argv[0] holds the name of the program itself and argv[1] is a pointer to the first command line argument supplied, and *argv[n] is the last argument. If no arguments are supplied, argc will be one, and if you pass one argument then argc is set at 2.

    You pass all the command line arguments separated by a space, but if argument itself has a space then you can pass such arguments by putting them inside double quotes “” or single quotes ”.

  • 2019-12-14-关于政府干预

    今天看到一篇文章,是李子阳老师写的,

    李子旸:香港社会制度的落后性

    https://mp.weixin.qq.com/s/4LwTqbj5JbB87va4WuDsgQ

    看了之后,我给奥派的张是之老师写了下面的留言。

    张老师,我也自认是一个奥派。10年之前,开始看铅笔研究社的文章。之前李子阳老师也是铅笔研究社的主笔之一。但是我感觉他现在的观点与奥派的观点已经有点不一致了。

    像这篇文章里面,他好像在主张政府需要规划和干预,而且似乎说的有道理,张老师怎么看呢?

    奥派一直强调是政府不要干预市场。但是就像文中说的一样,如果都是一些小业主组成的团体,是很难投资基础设施建设,制定长远的科技发展计划的,而基础设施和长远的科技计划显然是对经济发展有利的。

    举个例子,我舅舅所在的山村,一直想对外修一条水泥路。在我舅舅当村长以前,几十年都没有修成。等到我舅舅当村长以后,通过募捐,通过取得政府的支持,就把那条路给修通了,3.5公里的水泥路,总共耗资70万。

    如果按照奥派说的不干预市场,虽然大家都想修路,但是根本没有领头的人,或者领头的人不给力,这条路就修不好。我舅舅其实后来从政府里面基本上没有拿到钱,只是口头上支持而已。最后的钱也是我舅舅,通过项目村民募捐做出来的,但是他有村长的身份,大家会给他面子。我不知道这算不算政府干预市场。

    但是我觉得有的时候干预市场,真的是能够把一件做不成的事情变成能做得成。

    现在那条路修好之后,山里面土特产往外运就方便很多了。如果按照奥派的说法,市场会自发地形成力量去修筑一条路。我是不敢赞同的。

    比如跟我舅舅那个村子相邻的一个村子,那个路到现在还没有通。虽然那边出产的野樱桃比我们这边的还要好,如果修了路,他们肯定能赚的钱更多,但是就是没有人出头来干这件事情。

    我觉得是不是存在这种这样一种情况,比如,村子里面有1000个人,修一条路能给每个人都带来1000块钱的好处,这样总共就是100万好处,但是修这条路需要70万,理论上每个人都出700块钱就能赚到1000块钱的好处,但实际上根本没有人愿意出头做这件事,因为个人得到的好处太少了,一旦有了政府或者是一个强势的个人出面做这件事情,大家其实是愿意做这件事的。在这种情况下,政府出面干预其实是对经济的发展有好处的。不知道张老师同不同意我的看法?

  • 2019-12-13-the-c-programming-language-notes

    The C Programming Language Notes

    ch1 A Tutorial Introduction p22-p46

    1. Getting Started
      printf(“hello, world\n”);
    2. Variables and Arithmetic Expressions
      celsius = 5 * (fahr-32) / 9;
    3. The for statement
      for (fahr = 0; fahr <= 300; fahr = fahr + 20) printf(“%3d %6.1f\n”, fahr, (5.0/9.0)*(fahr-32));
    4. Symbolic Constants
      #define UPPER 300
    5. Character Input and Output c = getchar(); putchar(c);
      1. File Copying
      2. Character Counting
      3. Line Counting
      4. Word Counting
    6. Arrays
      int ndigit[10];
    7. Functions
      A function provides a convenient way to encapsulate some computation, which can then be used without worrying about its implementation.
    8. Arguments – Call by Value
      In C, all function arguments are passed “by value.” This means that the called function is given the values of its arguments in temporary variables rather than the originals.
    9. Character Arrays
      Character array is stored as an array of characters containing the characters in the string and terminated with a ‘\0’ to mark the end.
    10. External Variables and Scope
      An external variable must be defined, exactly once, outside of any function; this sets aside storage for it.

    ch2 Types, Operators and Expressions p47-p63

    1. Variable Names
      Names are made up of letters and digits; the first character must be a letter. The underscore “_” counts as a letter; it is sometimes useful for improving the readability of long variable names. Don’t begin variable names with underscore, however, since library routines often use such names. Upper and lower case letters are distinct, so x and X are two different names.
    2. Data Types and Sizes
      There are only a few basic data types in C: char, int, float, double
    3. Constants
      A long constant is written with a terminal l (ell) or L, as in 123456789L; an integer constant too big to fit into an int will also be taken as a long. Unsigned constants are written with a terminal u or U, and the suffix ul or UL indicates unsigned long.
      The first name in an enum has value 0, the next 1, and so on, unless explicit values are specified.
    4. Declarations
      All variables must be declared before use, although certain declarations can be made implicitly by content.
    5. Arithmetic Operators
      The binary arithmetic operators are +, -, *, /, and the modulus operator %.
    6. Relational and Logical Operators
      The relational operators are > >= < <=. Expressions connected by && or || are evaluated left to right, and evaluation stops as soon as the truth or falsehood of the result is known.
    7. Type Conversions
      When an operator has operands of different types, they are converted to a common type according to a small number of rules.
    8. Increment and Decrement Operators
      The increment operator ++ adds 1 to its operand, while the decrement operator — subtracts 1.
    9. Bitwise Operators
      The bitwise exclusive OR operator ^ sets a one in each bit position where its operands have different bits, and zero where they are the same.
    10. Assignment Operators and Expressions
      i += 2 is equivalent to i = i + 2
    11. Conditional Expressions
      z = (a > b) ? a : b;
    12. Precedence and Order of Evaluation
      x = f() + g();
      f may be evaluated before g or vice versa; thus if either f or g alters a variable on which the other depends, x can depend on the order of evaluation.

    ch3 Control Flow p64-p74

    1. Statements and Blocks
      x = 0; Braces { and } are used to group declarations and statements together into a compound statement, or block, so that they are syntactically equivalent to a single statement.
    2. If-Else
      if (a > b) z = a; else z = b;
    3. Else-If
      if (expression)
      statement
      else if (expression)
      statement
      else
      statement
    4. Switch
      switch (expression) {
      case const-expr: statements
      case const-expr: statements
      default: statements
      }
    5. Loops – While and For
      while (expression)
      statement
      for (expr1; expr2; expr3)
      statement
    6. Loops – Do-While
      do
      statement
      while (expression);
    7. Break and Continue
      The break statement provides an early exit from for, while, and do, just as from switch. The continue statement causes the next iteration of the enclosing for, while, or do loop to begin.
    8. Goto and labels
      C provides the infinitely-abusable goto statement, and labels to branch to.

    ch4 Functions and Program Structure p75-p95

    1. Basics of Functions
      cc main.c getline.c strindex.c -o grep.exe
    2. Functions Returning Non-integers
      double atof(char s[])
    3. External Variables
      A C program consists of a set of external objects, which are either variables or functions.
    4. Scope Rules
      If an external variable is to be referred to before it is defined, or if it is defined in a different source file from the one where it is being used, then an extern declaration is mandatory.
      in file1: extern int sp;
      in file2: int sp = 0;
    5. Header Files
      #include “calc.h”
    6. Static Variables
      The static declaration, applied to an external variable or function, limits the scope of that object to the rest of the source file being compiled.
    7. Register Variables
      A register declaration advises the compiler that the variable in question will be heavily used.
    8. Block Structure
      Declarations of variables (including initializations) may follow the left brace that introduces any compound statement, not just the one that begins a function.
    9. Initialization
      In the absence of explicit initialization, external and static variables are guaranteed to be initialized to zero; automatic and register variables have undefined (i.e., garbage) initial values.
      char pattern[] = { ‘o’, ‘u’, ‘l’, ‘d’, ‘\0’ };// char pattern = “ould”; wouldn’t work.
    10. Recursion
      C functions may be used recursively; that is, a function may call itself either directly or indirectly.
    11. The C Preprocessor
      1. File Inclusion
        If the filename is quoted, searching for the file typically begins where the source program was found; if it is not found there, or if the name is enclosed in < and >, searching follows an implementation-defined rule to find the file.
      2. Macro Substitution
        The scope of a name defined with #define is from its point of definition to the end of the source file being compiled.
        #define max(A, B) ((A) > (B) ? (A) : (B))
        The expressions are evaluated twice; this is bad if they involve side effects like increment operators or input and output.
        #define dprint(expr) printf(#expr ” = %g\n”, expr)
        If a parameter name is preceded by a # in the replacement text, the combination will be expanded into a quoted string with the parameter replaced by the actual argument.
      3. Conditional Inclusion
        It is possible to control preprocessing itself with conditional statements that are evaluated during preprocessing.

    ch5 pointer p96-125

    1. Pointers and Addresses
      int x=1, y;
      int *p; p=&x; y = *p;
    2. Pointers and Function Arguments
      void swap(int *px, int *py)
      swap(&a, &b);
    3. Pointers and Arrays
      int a[10], *pa, x;
      pa = &a[i]; // pa = a+i;
      x = *pa; // x= a[0];
      x = *(pa+1); // x = a[1]; x= *(a+1)
      f(&a[2]); // f(a+2)
    4. Address Arithmetic
      int *ptr; ptr = (int *)malloc(1024 * sizeof(ptr));
    5. Character Pointers and Functions
      char amessage[] = “now is the time”; /* an array */
      char *pmessage = “now is the time”; /* a pointer */
      void strcpy(char *s, char t){ while (s++ = *t++);
      }
    6. Pointer Arrays; Pointers to Pointers
      char lineptr[MAXLINES]; / pointers to text lines */
      lineptr[i] is a character pointer, *lineptr[i] is the character it points to.
    7. Multi-dimensional Arrays
      static char daytab[2][13] = {
      {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
      {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
      };
      int *daytab[13] is an array of 13 pointers to integers.
      daytab[i] is a integer pointer, *daytab[i] is the interger it points to.
    8. Initialization of Pointer Arrays
      char *month_name(int n)
      {
      static char *name[] = {
      “Illegal month”,
      “January”, “February”, “March”,
      “April”, “May”, “June”,
      “July”, “August”, “September”,
      “October”, “November”, “December”
      };
      return (n < 1 || n > 12) ? name[0] : name[n];
      }
    9. Pointers vs. Multi-dimensional Arrays
      int a[10][20]; //fixed size
      int *b[10]; // initialization must be done explicitly, either statically or with code.
    10. Command-line Arguments
      int main(int argc, char *argv[])
      For “echo hello, world” command, argc is 3, and argv[0], argv[1], and argv[2] are “echo”, “hello,”, and “world” respectively.
    11. Pointers to Functions
      void qsort(void lineptr[], int left, int right, int (comp)(void *, void *));
      int (*comp)(void *, void *) says that comp is a pointer to a function that has two void * arguments and returns an int.
      call pointer to function with
      (*comp)(v[i], v[left]) // comp(v[i],v[left]) also works, because a function’s name can also be used to get functions’ address.
    12. Complicated Declarations
      void comp() // comp: function returning pointer to void void (comp)() // comp: pointer to function returning void

    ch6 struct p126-p146

    1. Basics of Structures
      struct point {int x; int y;} x, y, z; // similar to int x, y, z;
    2. Structures and Functions
      legal operations to a struct: copy, assign it as a unit, take address by &, accessing its member.
      struct point makepoint(int x, int y)
      {
      struct point temp;
      temp.x = x;
      temp.y = y;
      return temp;
      }
      struct point pp; access x by(pp).x or pp->x;
    3. Arrays of Structures
      struct key {
      char *word;
      int count;
      } keytab[NKEYS];
      //equals
      struct key {
      char *word;
      int count;
      };
      struct key keytab[NKEYS];
    4. Pointers to Structures
      struct key *binsearch(char *word, struct key *tab, int n)
      pointer arithmetic that involves the first element beyond the end of an array (that is, &tab[n]) will work correctly.
    5. Self-referential Structures
      struct tnode{
      char *word;
      int count;
      struct tnode *left;
      struct tnode *right;
      }
    6. Table Lookup
      struct nlist *lookup(char *s)
      for (ptr = head; ptr != NULL; ptr = ptr->next)
    7. Typedef
      typedef int Length;
      typedef char *String;
      String p, lineptr[MAXLINES], alloc(int);
      int strcmp(String, String);
      p = (String) malloc(100);
    8. Unions
      union u_tag {
      int ival;
      float fval;
      char *sval;
      } u;
      struct {
      char *name;
      int flags;
      int utype;
      union {
      int ival;
      float fval;
      char *sval;
      } u;
      } symtab[NSYM];
    9. Bit-fields
      struct {
      unsigned int is_keyword : 1;
      unsigned int is_extern : 1;
      unsigned int is_static : 1;
      } flags;
      flags that contains three 1-bit fields

    ch7 Input and Output p147-p161

    1. Standard Input and Output
      while ((c = getchar()) != EOF) putchar(tolower(c));
    2. Formatted Output – printf
      int printf(char *format, arg1, arg2, …);
    3. Variable-length Argument Lists
      void minprintf(char *fmt, …)
    4. Formatted Input – Scanf
      int scanf(char *format, …)
    5. File Access
      FILE *fp;p
    6. Error Handling – Stderr and Exit
      fprintf(stderr, “%s: can’t open %s\n”, prog, *argv);
    7. Line Input and Output
      char *fgets(char *line, int maxline, FILE *fp)
      int fputs(char *line, FILE *fp)
    8. Miscellaneous Functions
      system(“date”);
      #define frand() ((double) rand() / (RAND_MAX+1.0))

    ch8 The UNIX System Interface p162-178

    1. File Descriptors
      prog outfile
    2. Low Level I/O – Read and Write
      int n_read = read(int fd, char *buf, int n);
      int n_written = write(int fd, char *buf, int n);
    3. Open, Creat, Close, Unlink
      int open(char *name, int flags, int perms);
      int creat(char *name, int perms);
    4. Random Access – Lseek
      long lseek(int fd, long offset, int origin);
    5. Example – An implementation of Fopen and Getc
      FILE *fopen(char *name, char *mode)
    6. Example – Listing Directories
      void fsize(char *name)
      void dirwalk(char *dir, void (*fcn)(char *))
    7. Example – A Storage Allocator
      void *malloc(unsigned nbytes)

    Appendix A: Reference Manual p179-p233

    1. Introduction
    2. Lexical Conventions
      There are six classes of tokens: identifiers, keywords, constants, string literals, operators, and other separators.
      Upper and lower case letters are different.
      Keywords auto double int struct break else long switch case enum register typedef char extern return union const float short unsigned continue for signed void default goto sizeof volatile do if static while
    3. Syntax Notation
      { expression\sub(opt) } means an optional expression, enclosed in braces.
    4. Meaning of Identifiers
      Identifiers, or names, refer to a variety of things: functions; tags of structures, unions, and enumerations; members of structures or unions; enumeration constants; typedef names; and objects.
      There are two storage classes: automatic and static.
    5. Objects and Lvalues
      An Object is a named region of storage; an lvalue is an expression referring to an object.
    6. Conversions
      Integral Promotion, Integral Conversions, Integer and Floating, Floating Types, Arithmetic Conversions, Pointers and Integers
    7. Expressions
      A pair of expressions separated by a comma is evaluated left-to-right, and the value of the left expression is discarded.
    8. Declarations
      In a declaration T D:
      When D is an unadored identifier, the type of the identifier is T.
      When D has the form ( D1 ) then the type of the identifier in D1 is the same as that of D. The parentheses do not alter the type, but may change the binding of complex declarators.
      int i, *pi, *const cpi = &i;
      const int ci = 3, *pci;
      declare an integer i and a pointer to an integer pi. The value of the constant pointer cpi may not be changed; it will always point to the same location, although the value to which it refers may be altered. The integer ci is constant, and may not be changed (though it may be initialized, as here.) The type of pci is “pointer to const int,” and pci itself may be changed to point to another place, but the value to which it points may not be altered by assigning through pci.
    9. Statements
      Except as described, statements are executed in sequence.
    10. External Declarations
      The unit of input provided to the C compiler is called a translation unit; it consists of a sequence of external declarations, which are either declarations or function definitions.
    11. Scope and Linkage
      A program need not all be compiled at one time: the source text may be kept in several files containing translation units, and precompiled routines may be loaded from libraries.
    12. Preprocessor
      A preprocessor performs macro substitution, conditional compilation, and inclusion of named files.
    13. Grammar
      A recapitulation of the grammar that was given throughout the earlier part of this appendix.

    Appendix B – Standard Library p234-p296

    1. Input and Output: The input and output functions, types, and macros defined in represent nearly one third of the library.
      1. File Operations
      2. Formatted Output
      3. Formatted Input
      4. Character Input and Output Functions
      5. Direct Input and Output Functions
      6. File Positioning Functions
      7. Error Functions
    2. Character Class Tests:
      The header declares functions for testing characters.
    3. String Functions:
      There are two groups of string functions defined in the header . The first have names beginning with str; the second have names beginning with mem.
    4. Mathematical Functions:
      The header declares mathematical functions and macros.
    5. Utility Functions:
      The header declares functions for number conversion, storage allocation, and similar tasks.
    6. Diagnostics:
      The assert macro is used to add diagnostics to programs.
    7. Variable Argument Lists:
      The header provides facilities for stepping through a list of function arguments of unknown number and type.
    8. Non-local Jumps:
      The declarations in provide a way to avoid the normal function call and return sequence, typically to permit an immediate return from a deeply nested function call.
    9. Signals:
      The header provides facilities for handling exceptional conditions that arise during execution, such as an interrupt signal from an external source or an error in execution.
    10. Date and Time Functions:
      The header declares types and functions for manipulating date and time.
    11. Implementation-defined Limits: and
      The header defines constants for the sizes of integral types. The values below are acceptable minimum magnitudes; larger values may be used.
  • 2019-12-01-twitter视频下载

    今天在twitter上面看到一个好玩的视频,我就想下载下来,结果折腾了几个小时。

    首先看网页代码,视频那里是一个blob:

    <video preload="none" playsinline="" aria-label="Embedded video" poster="https://pbs.twimg.com/ext_tw_video_thumb/1200926756321951745/pu/img/l-V1sNMeAZL5tcGN.jpg" src="blob:https://twitter.com/dd866381-2ada-4c2b-80af-9cec2a580039" style="width: 100%; height: 100%; position: absolute; background-color: black; top: 0%; left: 0%; transform: rotate(0deg) scale(1.005);"></video>

    我google了一下,这是流媒体的格式,要下载的话需要找到对应的m3u8文件,就是在chrom浏览器的Network里面过滤m3u8. 看到一个https://video.twimg.com/ext_tw_video/1200926756321951745/pu/pl/460×258/Kuir9CTQnfaw-EWA.m3u8

    Google说可以用youtube-dl下载,然后我先去下载这个玩意,因为还有同学说可以直接用ffmpeg转换格式。

    等youtube-dl下载完了之后,我运行下面的命令:

    ./youtube-dl -f mp4 -o baby.mp4 https://video.twimg.com/ext_tw_video/1200926756321951745/pu/pl/460x258/Kuir9CTQnfaw-EWA.m3u8

    结果提示需要ffmpeg或者avconv,尼玛还是得安装ffmpeg。

    然后我就老实的brew install ffmpeg。完事之后。

    运行:

    ./youtube-dl -f mp4 -o baby.mp4 https://video.twimg.com/ext_tw_video/1200926756321951745/pu/pl/460x258/Kuir9CTQnfaw-EWA.m3u8

    结果卡在那儿不动弹。

    我就试一下直接用ffmpeg转换:

    ffmpeg -i 'https://video.twimg.com/ext_tw_video/1200926756321951745/pu/pl/460x258/Kuir9CTQnfaw-EWA.m3u8' -vcodec copy -acodec copy -absf aac_adtstoasc o.mp4

    也是一样的,下到一半卡在那儿不动弹。

    我就换了个思路,想先看看m3u8里面的内容,

    wget https://video.twimg.com/ext_tw_video/1200926756321951745/pu/pl/460x258/Kuir9CTQnfaw-EWA.m3u8
    cat Kuir9CTQnfaw-EWA.m3u8

    结果是这样:

    #EXTM3U
    #EXT-X-VERSION:6
    #EXT-X-MEDIA-SEQUENCE:0
    #EXT-X-TARGETDURATION:4
    #EXT-X-PLAYLIST-TYPE:VOD
    #EXT-X-ALLOW-CACHE:YES
    #EXTINF:3.000,
    /ext_tw_video/1200926756321951745/pu/vid/0/3000/460x258/G5KaOzbFFR97qdzl.ts
    #EXTINF:3.000,
    /ext_tw_video/1200926756321951745/pu/vid/3000/6000/460x258/4lEqbRtnJ2YtXHRw.ts
    #EXTINF:3.000,
    /ext_tw_video/1200926756321951745/pu/vid/6000/9000/460x258/uSvCKT4MckCDGKSf.ts
    #EXTINF:3.000,
    /ext_tw_video/1200926756321951745/pu/vid/9000/12000/460x258/QSnzs6dHUar2OXfY.ts
    #EXTINF:3.000,
    /ext_tw_video/1200926756321951745/pu/vid/12000/15000/460x258/zdytmI8intABhyFW.ts
    #EXTINF:3.000,
    /ext_tw_video/1200926756321951745/pu/vid/15000/18000/460x258/Gh8owrBVf_s8SwDs.ts
    #EXTINF:3.000,
    /ext_tw_video/1200926756321951745/pu/vid/18000/21000/460x258/GWnvSye6J8mkjr35.ts
    #EXTINF:3.000,
    /ext_tw_video/1200926756321951745/pu/vid/21000/24000/460x258/Bmd-gN7sTAtj_bme.ts
    #EXTINF:3.000,
    /ext_tw_video/1200926756321951745/pu/vid/24000/27000/460x258/lkTRSPSKrGpFB_h6.ts
    #EXTINF:3.000,
    /ext_tw_video/1200926756321951745/pu/vid/27000/30000/460x258/DcZQHhRTJOJ-HNyJ.ts
    #EXTINF:3.000,
    /ext_tw_video/1200926756321951745/pu/vid/30000/33000/460x258/-3GD9_NdfoDUVF9q.ts
    #EXTINF:3.000,
    /ext_tw_video/1200926756321951745/pu/vid/33000/36000/460x258/WuuI_REDOMnYjzSU.ts
    #EXTINF:3.958,
    /ext_tw_video/1200926756321951745/pu/vid/36000/39958/460x258/AZrLcuvgp5XsaOcU.ts
    #EXT-X-ENDLIST

    我就写了个脚本wget.sh先把这个视频下载下来再转换:

    wget https://video.twimg.com/ext_tw_video/1200926756321951745/pu/vid/0/3000/460x258/G5KaOzbFFR97qdzl.ts
    wget https://video.twimg.com/ext_tw_video/1200926756321951745/pu/vid/3000/6000/460x258/4lEqbRtnJ2YtXHRw.ts
    wget https://video.twimg.com/ext_tw_video/1200926756321951745/pu/vid/6000/9000/460x258/uSvCKT4MckCDGKSf.ts
    wget https://video.twimg.com/ext_tw_video/1200926756321951745/pu/vid/9000/12000/460x258/QSnzs6dHUar2OXfY.ts
    wget https://video.twimg.com/ext_tw_video/1200926756321951745/pu/vid/12000/15000/460x258/zdytmI8intABhyFW.ts
    wget https://video.twimg.com/ext_tw_video/1200926756321951745/pu/vid/15000/18000/460x258/Gh8owrBVf_s8SwDs.ts
    wget https://video.twimg.com/ext_tw_video/1200926756321951745/pu/vid/18000/21000/460x258/GWnvSye6J8mkjr35.ts
    wget https://video.twimg.com/ext_tw_video/1200926756321951745/pu/vid/21000/24000/460x258/Bmd-gN7sTAtj_bme.ts
    wget https://video.twimg.com/ext_tw_video/1200926756321951745/pu/vid/24000/27000/460x258/lkTRSPSKrGpFB_h6.ts
    wget https://video.twimg.com/ext_tw_video/1200926756321951745/pu/vid/27000/30000/460x258/DcZQHhRTJOJ-HNyJ.ts
    wget https://video.twimg.com/ext_tw_video/1200926756321951745/pu/vid/30000/33000/460x258/-3GD9_NdfoDUVF9q.ts
    wget https://video.twimg.com/ext_tw_video/1200926756321951745/pu/vid/33000/36000/460x258/WuuI_REDOMnYjzSU.ts
    wget https://video.twimg.com/ext_tw_video/1200926756321951745/pu/vid/36000/39958/460x258/AZrLcuvgp5XsaOcU.ts

    下载下来之后,因为m3u8里面的路径也要改一下:
    把前面的“/ext_tw_video/1200926756321951745/pu/vid/33000/36000/460×258/”这一串也去掉。变成k.m3u8。

    #EXTM3U
    #EXT-X-VERSION:6
    #EXT-X-MEDIA-SEQUENCE:0
    #EXT-X-TARGETDURATION:4
    #EXT-X-PLAYLIST-TYPE:VOD
    #EXT-X-ALLOW-CACHE:YES
    #EXTINF:3.000,
    G5KaOzbFFR97qdzl.ts
    #EXTINF:3.000,
    4lEqbRtnJ2YtXHRw.ts
    #EXTINF:3.000,
    uSvCKT4MckCDGKSf.ts
    #EXTINF:3.000,
    QSnzs6dHUar2OXfY.ts
    #EXTINF:3.000,
    zdytmI8intABhyFW.ts
    #EXTINF:3.000,
    Gh8owrBVf_s8SwDs.ts
    #EXTINF:3.000,
    GWnvSye6J8mkjr35.ts
    #EXTINF:3.000,
    Bmd-gN7sTAtj_bme.ts
    #EXTINF:3.000,
    lkTRSPSKrGpFB_h6.ts
    #EXTINF:3.000,
    DcZQHhRTJOJ-HNyJ.ts
    #EXTINF:3.000,
    -3GD9_NdfoDUVF9q.ts
    #EXTINF:3.000,
    WuuI_REDOMnYjzSU.ts
    #EXTINF:3.958,
    AZrLcuvgp5XsaOcU.ts
    #EXT-X-ENDLIST

    然后再来用ffmpeg -i k.m3u8 k.mp4,就搞定了。

    总结一下, ffmpeg这个肯定是需要安装的,youtube-dl是调用这个的。如果网络给力的话,youtube-dl应该可以直接完成转换,但是我试了两次就没成功,所以用wget先把ts文件下载下来再转换。

  • 2019-11-12-比特币定投时机分析

    比特币定投时机分析

    前几天我写了比特币定投的文章,我当时选的是每个月第一天买入1000块钱比特币,然后我就想如果是在其它日期买入收益是怎样呢。然后代码如下:

    echo 'simple policy: buy 140 usd on the first day of everymonth:'.PHP_EOL;
    $btc = [];
    $money = [];
    $max = [];
    for ($i=0;$i<32;$i++) {
        $btc[$i] = $money[$i] = 0;
    }
    foreach ($result as $row) {
        $i = ltrim(explode("-", $row[$keys[0]])[2], "0");
        $price = $row[$keys[1]];
        $spend = 140;
        $btc[$i] += $spend/$price;
        $money[$i] += $spend;
    }
    for ($i = 1; $i < sizeof($btc); $i++) {
        echo 'on '.$i.'st day of month buy '.$btc[$i].' BTC for '.$money[$i].' USD';
        echo '. ROI is '.($last_price*$btc[$i]/$money[$i]).PHP_EOL;
    }
    echo PHP_EOL;

    结果如下:

    simple policy: buy 140 usd on different days of everymonth:
    on 1st day of month buy 22.171847894906 BTC for 11060 USD. ROI is 18.745575707508
    on 2st day of month buy 22.483835796744 BTC for 11060 USD. ROI is 19.009351323391
    on 3st day of month buy 22.683132436126 BTC for 11060 USD. ROI is 19.177850144936
    on 4st day of month buy 22.873776562158 BTC for 11060 USD. ROI is 19.339033548082
    on 5st day of month buy 22.663165006578 BTC for 11060 USD. ROI is 19.160968333188
    on 6st day of month buy 22.829619605275 BTC for 11060 USD. ROI is 19.301700278335
    on 7st day of month buy 22.895282529045 BTC for 10920 USD. ROI is 19.605385580803
    on 8st day of month buy 22.758523723982 BTC for 10920 USD. ROI is 19.488278089274
    on 9st day of month buy 22.772445297502 BTC for 10920 USD. ROI is 19.500199227019
    on 10st day of month buy 22.693920417488 BTC for 10920 USD. ROI is 19.432957840135
    on 11st day of month buy 22.499652101912 BTC for 10920 USD. ROI is 19.266604565037
    on 12st day of month buy 22.281754478063 BTC for 10920 USD. ROI is 19.08001735314
    on 13st day of month buy 22.068019586461 BTC for 10920 USD. ROI is 18.896994717075
    on 14st day of month buy 22.068686013953 BTC for 10920 USD. ROI is 18.897565383452
    on 15st day of month buy 22.379413146748 BTC for 10920 USD. ROI is 19.163643132924
    on 16st day of month buy 22.212017346347 BTC for 10920 USD. ROI is 19.020300974674
    on 17st day of month buy 22.181794017372 BTC for 10920 USD. ROI is 18.994420533262
    on 18st day of month buy 21.982156192808 BTC for 10920 USD. ROI is 18.823469311231
    on 19st day of month buy 22.179971247115 BTC for 10920 USD. ROI is 18.992859682739
    on 20st day of month buy 21.974796783756 BTC for 10920 USD. ROI is 18.817167399388
    on 21st day of month buy 21.872727840678 BTC for 10920 USD. ROI is 18.729765071755
    on 22st day of month buy 21.729407841843 BTC for 10920 USD. ROI is 18.607039185536
    on 23st day of month buy 21.647836522888 BTC for 10920 USD. ROI is 18.537189112343
    on 24st day of month buy 21.504258966867 BTC for 10920 USD. ROI is 18.41424267816
    on 25st day of month buy 21.636898731196 BTC for 10920 USD. ROI is 18.527823007195
    on 26st day of month buy 21.575032925006 BTC for 10920 USD. ROI is 18.474846898118
    on 27st day of month buy 21.486862408557 BTC for 10920 USD. ROI is 18.399345887391
    on 28st day of month buy 22.420571564532 BTC for 11060 USD. ROI is 18.955863474289
    on 29st day of month buy 21.492371373023 BTC for 10360 USD. ROI is 19.398877480778
    on 30st day of month buy 20.996120074596 BTC for 10220 USD. ROI is 19.210566469857
    on 31st day of month buy 12.562011754005 BTC for 6440 USD. ROI is 18.240021560587

    结论是除了31号以外没有太大差别,因为不是每个月都有31号。

    我又想到另外一个问题,就是这个简单策略从不同的时候开始对应的收益是多少呢?

    for($year = 2013; $year < 2020; $year++){
        $btc = 0;
        $money = 0;
        foreach ($result as $row) {
            if (strtotime($row[$keys[0]]) < strtotime($year.'-01-01')) {
                continue;
            }
            if (explode("-", $row[$keys[0]])[2]=='01') {
                $price = $row[$keys[1]];
                $btc += 140/$price;
                $money += 140;
            }
        }
        echo 'start from '.$year;
        echo ' buy '.$btc.' BTC for '.$money.' USD';
        echo '. ROI is '.($last_price*$btc/$money).PHP_EOL;
    }

    结果如下:

    start from 2013 buy 22.171847894906 BTC for 11060 USD. ROI is 18.745575707508
    start from 2014 buy 14.422215949735 BTC for 9940 USD. ROI is 13.567431233189
    start from 2015 buy 11.074658072271 BTC for 8260 USD. ROI is 12.537250354511
    start from 2016 buy 4.6269273232684 BTC for 6580 USD. ROI is 6.5753489208709
    start from 2017 buy 1.399094962316 BTC for 4900 USD. ROI is 2.6699500225044
    start from 2018 buy 0.49292426430722 BTC for 3220 USD. ROI is 1.4314505327275
    start from 2019 buy 0.26423599874152 BTC for 1540 USD. ROI is 1.6044392685404

    结论当然是开始定投的时间越晚,收益越低。

    同样如果动态调整买入数量,得到结果是:

    on 1st day of month buy 54.68896148375 BTC for 24198.152323714 USD. ROI is 21.133405659588
    on 2st day of month buy 48.267456335385 BTC for 21573.03055174 USD. ROI is 20.921618237196
    on 3st day of month buy 51.927616303556 BTC for 23261.473649997 USD. ROI is 20.874360617497
    on 4st day of month buy 54.068655783724 BTC for 23821.542164162 USD. ROI is 21.224023525604
    on 5st day of month buy 56.741726914806 BTC for 24986.400799988 USD. ROI is 21.234931601517
    on 6st day of month buy 53.217372045735 BTC for 24506.095243249 USD. ROI is 20.306324724596
    on 7st day of month buy 49.480430048546 BTC for 23050.151453025 USD. ROI is 20.072973050566
    on 8st day of month buy 43.009364229676 BTC for 21216.65825486 USD. ROI is 18.955622929084
    on 9st day of month buy 44.911577713843 BTC for 21182.432421281 USD. ROI is 19.825972595816
    on 10st day of month buy 46.503338955097 BTC for 21126.39968875 USD. ROI is 20.583094305775
    on 11st day of month buy 49.933543814475 BTC for 22750.574451838 USD. ROI is 20.523529101953
    on 12st day of month buy 46.670234156205 BTC for 22531.934998415 USD. ROI is 19.36838946566
    on 13st day of month buy 44.246103969017 BTC for 22059.150509462 USD. ROI is 18.755915647944
    on 14st day of month buy 45.929917738979 BTC for 22048.423270757 USD. ROI is 19.479156609693
    on 15st day of month buy 46.619072138652 BTC for 22018.097956519 USD. ROI is 19.798662171002
    on 16st day of month buy 45.994781276714 BTC for 22525.160165509 USD. ROI is 19.093814083309
    on 17st day of month buy 44.01241958069 BTC for 22716.862562778 USD. ROI is 18.11669251188
    on 18st day of month buy 42.904043520119 BTC for 22212.986120989 USD. ROI is 18.061062625519
    on 19st day of month buy 45.26385493709 BTC for 22631.051873757 USD. ROI is 18.702463569818
    on 20st day of month buy 45.857495755562 BTC for 22263.487782646 USD. ROI is 19.260570739059
    on 21st day of month buy 45.105945523037 BTC for 21563.342732128 USD. ROI is 19.560039371102
    on 22st day of month buy 44.572688867049 BTC for 21267.952653427 USD. ROI is 19.597251599066
    on 23st day of month buy 43.274217689149 BTC for 20151.421970835 USD. ROI is 20.080547395047
    on 24st day of month buy 42.184610969101 BTC for 20127.810971946 USD. ROI is 19.597899330555
    on 25st day of month buy 41.603369954415 BTC for 19646.816392842 USD. ROI is 19.801055612622
    on 26st day of month buy 43.569844882186 BTC for 20399.918765522 USD. ROI is 19.971449891362
    on 27st day of month buy 46.073866974541 BTC for 22240.262415163 USD. ROI is 19.37165724189
    on 28st day of month buy 49.860591240701 BTC for 23312.017117417 USD. ROI is 19.999981317215
    on 29st day of month buy 48.409926306664 BTC for 21470.517141603 USD. ROI is 21.083559590936
    on 30st day of month buy 51.125198054587 BTC for 22318.755862658 USD. ROI is 21.419880376601
    on 31st day of month buy 23.662189356623 BTC for 11086.585427419 USD. ROI is 19.957637817137

    start from 2013 buy 54.68896148375 BTC for 24198.15 USD. ROI is 21.133405659588
    start from 2014 buy 34.138451267446 BTC for 18978.9 USD. ROI is 16.819955805149
    start from 2015 buy 13.124232059274 BTC for 11972.62 USD. ROI is 10.250300012662
    start from 2016 buy 5.0865272908385 BTC for 9908.83 USD. ROI is 4.800108353695
    start from 2017 buy 1.9449513947794 BTC for 8228.12 USD. ROI is 2.2103466625796
    start from 2018 buy 1.2345328381095 BTC for 6817.58 USD. ROI is 1.6932623616336
    start from 2019 buy 0.22404450624699 BTC for 1383.58 USD. ROI is 1.5141907571945

    跟简单策略类似,哪一天买入对结果影响不大,同样买入越晚收益越低。

    我就换了一个思路,不是每个月固定时间投,而是每次价格下跌5%,我就买入1000块钱:

    echo 'buy when price down policy:'.PHP_EOL;
    $btc = 0;
    $money = 0;
    foreach($result as $i => $row){
        if($i == 0){continue;}
        $yesterday_price = ($result[$i-1][$keys[1]] + $result[$i-1][$keys[4]]) / 2;
        $price = $row[$keys[1]];
        if($price < $yesterday_price*0.95){
            $buy = 140;
            $btc += $buy/$price;
            $money += $buy;
            echo $row[$keys[0]].' '.$row[$keys[1]].' '.$row[$keys[4]].' ';
            echo 'buy '.$buy/$price.' BTC for '.$buy.' USD'.PHP_EOL;
        }
    }
    echo 'BTC: '.$btc.PHP_EOL;
    echo 'Spend money: '.$money.PHP_EOL;
    echo 'Final value: '.($btc*$last_price).PHP_EOL;
    echo 'Final ROI: '.(($btc*$last_price)/$money).PHP_EOL;
    echo PHP_EOL;

    结果如下:

    BTC: 16.835854779097
    Spend money: 7840
    Final value: 157429.88937822
    Final ROI: 20.080343032936

    就是花了7840usd(56000人民币)买了16.835854779097个比特币,价值157429.89usd(1124499.21人民币),收益率20.08倍收益。
    可以看到这种方案跟简单定投方案的收益率差不多。

    不过这种方案收益率跟起投时间的关系更密切:

    echo 'buy when price down policy:'.PHP_EOL;
    for($year = 2013; $year < 2020; $year++){
        $btc = 0;
        $money = 0;
        foreach($result as $i => $row){
            if (strtotime($row[$keys[0]]) < strtotime($year.'-01-01')) {
                continue;
            }
            if($i == 0){continue;}
            $yesterday_price = ($result[$i-1][$keys[1]] + $result[$i-1][$keys[4]]) / 2;
            $price = $row[$keys[1]];
            if($price < $yesterday_price*0.95){
                $buy = 140;
                $btc += $buy/$price;
                $money += $buy;
            }
        }
        echo 'start from '.$year;
        echo ' buy '.$btc.' BTC for '.$money.' USD';
        echo '. ROI is '.($last_price*$btc/$money).PHP_EOL;
    }

    结果是:

    start from 2013 buy 16.835854779097 BTC for 7840 USD. ROI is 20.080343032936
    start from 2014 buy 7.2568342363667 BTC for 5880 USD. ROI is 11.540427475479
    start from 2015 buy 5.5110740890794 BTC for 4900 USD. ROI is 10.51700762599
    start from 2016 buy 2.0373331871337 BTC for 4060 USD. ROI is 4.6923245762494
    start from 2017 buy 1.2092733133323 BTC for 3640 USD. ROI is 3.106526798747
    start from 2018 buy 0.30747030774609 BTC for 2380 USD. ROI is 1.2080314607537
    start from 2019 buy 0.070211328288695 BTC for 700 USD. ROI is 0.93791000479273

    如果是从2019开始用这种策略投资,收益是负的。因为2019没有几次买入的机会。

    前面的几种策略里面我都是只有买入,如果加入买出能否获得更多收益呢?
    代码:

    echo 'buy when price down policy and sell when price up:'.PHP_EOL;
    $max_roi = 0;
    for ($buy_ratio = 0.1; $buy_ratio <= 1; $buy_ratio += 0.01) {
        for ($sell_ratio = 0; $sell_ratio <= 0.1; $sell_ratio += 0.01) {
            $btc = 0;
            $init_money = 11060;
            $money = $init_money;
            foreach ($result as $i => $row) {
                if (0 == $i) {
                    continue;
                }
                $yesterday_price = ($result[$i - 1][$keys[1]] + $result[$i - 1][$keys[4]]) / 2;
                if (strtotime($row[$keys[0]]) < strtotime('2013-01-01')) {
                    continue;
                }
                $price = $row[$keys[1]];
                if ($price < $yesterday_price * 0.95) {
                    //best buy ratio is 0.5 for 2013-2019
                    $buy = $money * $buy_ratio;
                    if ($buy < 10) {
                        continue;
                    }
                    $money -= $buy;
                    $btc += $buy / $price;
                }
                if ($price > $yesterday_price * 1.05) {
                    // best sell ratio is 0 for 2013-2019
                    $sell = $btc * $sell_ratio;
                    if ($sell < 0.01) {
                        continue;
                    }
                    $btc -= $sell;
                    $money += $price * $sell;
                }
            }
            //echo 'BTC: '.$btc.PHP_EOL;
            //echo 'money: '.$money.PHP_EOL;
            //echo 'Final value: '.($btc*$last_price+$money).PHP_EOL;
            echo 'Buy Ratio: '.$buy_ratio.' , sell ratio '.$sell_ratio.' get '.$btc.' BTC with '.$money.' ';
            $roi = ($btc * $last_price + $money) / $init_money;
            $max_roi = $roi > $max_roi ? $roi : $max_roi;
            echo 'Final ROI: '.$roi.PHP_EOL;
            //echo PHP_EOL;
        }
    }
    echo 'max roi :'.$max_roi.PHP_EOL;

    我的策略很简单,就是价格下跌了5%就买入全部金额的一定比例比特币,价格上涨了5%就卖出一定比例比特币。

    结果发现买入比例49%,卖出比例0的时候能得到最高的ROI, 93.036942129554。这个比例高于直接起始的时候直接买入比特币的策略,因为有一部分的比特币是降价之后才买的。同时卖出比例为0,说明这个策略里面不能卖出比特币。其实也很好理解,因为比特币总体是上涨的,一旦你卖出,因为价格升高,你后面买回来的比特币就越少。总体收益也就减少了。

    同时这种策略跟市场紧密相关,

    如果从2013开始买入,最佳买入比例是0.49, 最佳卖出比例是0.

    如果从2014开始买入,最佳买入比例是0.13, 最佳卖出比例是0.

    如果从2015开始买入,最佳买入比例是0.46, 最佳卖出比例是0.

    如果从2016开始买入,最佳买入比例是0.99, 最佳卖出比例是0.

    如果从2017开始买入,最佳买入比例是0.56, 最佳卖出比例是0.

    如果从2018开始买入,最佳买入比例是0.09, 最佳卖出比例是0.01.

    如果从2019开始买入,最佳买入比例是0, 最佳卖出比例是0.

    所以这种策略没有太多的价值,因为我们无从判断未来行情走向。同时如果从2019年开始用这种策略,最优选择是不要买。:)

    类似,如果记录上一次的卖出价格做为参考点:

    如果从2013开始买入,
    Buy Ratio: 0.45 , sell ratio 0 get 102.70933163813 BTC with 306.1478853125 Final ROI: 86.865077379783
    如果从2014开始买入,
    Buy Ratio: 0.16 , sell ratio 0 get 21.895526577296 BTC with 1146.5311163133 Final ROI: 18.615619685547
    如果从2015开始买入,
    Buy Ratio: 0.61 , sell ratio 0 get 40.768021693446 BTC with 255.8665746 Final ROI: 34.491169763761
    如果从2016开始买入,
    Buy Ratio: 0.99 , sell ratio 0 get 29.992604158107 BTC with 110.59999999999 Final ROI: 25.367770564549
    如果从2017开始买入,
    Buy Ratio: 0.92 , sell ratio 0 get 12.312205165812 BTC with 70.783999999999 Final ROI: 10.415968708755
    如果从2018开始买入,
    Buy Ratio: 0.1 , sell ratio 0 get 1.2671786688341 BTC with 2811.3036061319 Final ROI: 1.3255449010102
    如果从2019开始买入,
    Buy Ratio: 0.96 , sell ratio 0 get 3.0243124957152 BTC with 17.695999999999 Final ROI: 2.5585577745758

    跟上一种策略的结果类似,没有太大价值。

    我还不死心,又想到一个策略,就是下跌5%就买1000块,上涨5%就卖掉5%。

    echo 'buy when price down policy and sell when price up:'.PHP_EOL;
    $btc = 0;
    $invest = 0;
    $return = 0;
    foreach($result as $i => $row){
        if($i == 0){continue;}
        $yesterday_price = ($result[$i-1][$keys[1]] + $result[$i-1][$keys[4]]) / 2;
        $price = $row[$keys[1]];
        if($price < $yesterday_price*0.95){
            $buy = 140;
            $btc += $buy/$price;
            $invest += $buy;
            echo $row[$keys[0]].' '.$row[$keys[1]].' '.$row[$keys[4]].' ';
            echo 'buy '.$buy/$price.' BTC for '.$buy.' USD'.PHP_EOL;
        }
        if($price > $yesterday_price*1.05){
            $sell = $btc * 0.05;
            if($sell == 0){continue;}
            $btc -= $sell;
            $return += $price*$sell;
            echo $row[$keys[0]].' '.$row[$keys[1]].' '.$row[$keys[4]].' ';
            echo 'sell '.$sell.' BTC for '.($price*$sell).' USD'.PHP_EOL;
        }
    }
    echo 'BTC: '.$btc.PHP_EOL;
    echo 'Invest money: '.$invest.PHP_EOL;
    echo 'Final value: '.($btc*$last_price+$return).PHP_EOL;
    echo 'Final ROI: '.(($btc*$last_price+$return)/$invest).PHP_EOL;
    echo PHP_EOL;

    结果是

    BTC: 3.2084003878113
    Invest money: 7840
    Final value: 76196.536163102
    Final ROI: 9.7189459391712

    就是花了7840usd(56000人民币)买了3.2084003878113个比特币,价值76196.53usd(544260.93人民币)加上中间卖出比特币获得了46195.20usd(329965.71人民币),9.72倍收益。

    可以看出这种策略的收益率不到每逢价格下跌5%就买入1000块钱的策略的一半,不过这种策略的优势在于,投入的资金小于卖出比特币获得的资金,也就是很多时候你是用你的盈利来投资,所以比较能够承受住价格波动带来的损失。这种策略里面卖出比例为0的时候收益率最高,也就变成了每逢价格下跌5%就买入1000块钱的策略。

    综合所有的结果,我的结论是老老实实也定投,各种策略能跑赢定投的不多。定投的日期也无所谓,只要不是每个月31号就行。

  • 2019-11-08-关于定投的思考

    昨天我写了一篇比特币定投收益分析的文章,发到群里面之后,小伙伴们纷纷发表意见。其中两种意见比较有价值。

    一种意见是历史不代表未来,我是同意这一点的。

    因为未来是无法预测的,定投是否成功的关键还在于选择的标的,说的更直接一点在于你自己的判断和对这个判断的信心。

    如果判断失误,定投是没有意义的,而且可能投的越多亏的越多。

    至于怎么做出判断,每个人都有自己的标准,当然有些是理性的,有些只是碰运气,没有人能百分之百判断正确。

    比较靠谱的方法是看长期的趋势,就是巴菲特所说的价值投资,标的短期价格没有任何人能够预测,但是长期来看是跟它本身的价值一致的。

    可惜长期趋势也不是那么容易判断的,在某一个时间点,我们其实是没有足够的信息判断所谓的长期趋势向上还是向下,掌握的信息越多当然越有利,但是没有人敢说自己有百分之百的把握。

    即使是一些明显会向上的行业,具体到你投资的那个标的是否能够增长还是不一定的。比如按照现在的中国人口变化趋势,中国老龄人口比例是不断升高的,所以20年内养老行业肯定是上升趋势的,可是没有哪个标的叫做养老行业。

    你只能具体到某一个公司,这个公司可能是做疗养院,养老院,保健品或者其他跟养老行业有关的事情。

    一旦涉及到具体的公司,你要考虑的就不仅仅是整个长期的趋势了,你要考虑运作这个公司的人是否靠谱,公司的运营方法是否得当,公司的成本管理是否合理,公司有没有受到政策的影响等等一系列的因素。

    这其实是一件很专业的事情。巴菲特就是因为擅长这件事变成世界最有钱的几个人之一。

    另外一种意见说定投是没有意义的,就像昨天那篇文章里面每个月1000块钱投79个月,有个小伙伴说你还不如开始定投的那个月一次拿79000块钱买成比特币,删掉软件,放到今天再看。

    我稍微算了一下,如果这么操作的话,收益有69.38倍,比我用定投的收益20.1倍强多了。

    我差点同意了他的意见,感觉只要选择标的是对的,而且你有信心,直接一把梭哈收益比定投还要好。

    不过我又回头仔细想了一下,这么思考是不对的,因为你一把梭哈,结果是不太确定的,如果你在2017年最高点梭哈你就悲剧了,因为直到今天价格(2019-11-08, 9,242.60USD)也不到最高点(2017-12-17, 19308.3USD)的一半,你的收益是-52%,就是亏了一半多,俗称腰斩。

    而如果用定投的话,即使你是在最高点那一天买入,然后每个月再投1000块钱,到今天为止收益也有60%。

    $btc = 0;
    $money = 0;
    $init_price = ($result[0][$keys[1]]+$result[0][$keys[4]])/2;
    echo 'initial price on '.$result[0][$keys[0]].': '.$init_price.PHP_EOL;
    foreach ($result as $row) {
        if (strtotime($row[$keys[0]])>strtotime('2017-11-30')){
            if (explode("-", $row[$keys[0]])[2]=='17') {
                $price = ($row[$keys[1]]+$row[$keys[4]])/2;
                $spend = 140*($init_price/$price);
                $btc += $spend/$price;
                $money += $spend;
                echo $row[$keys[0]].' '.$row[$keys[1]].' '.$row[$keys[4]].' ';
                echo 'buy '.$spend/$price.' BTC for '.$spend.' USD'.PHP_EOL;
                $init_price = $price > $init_price ? $price: $init_price;
            }
        }
    }
    echo 'total:'.PHP_EOL;
    echo 'buy '.$btc.' BTC for '.$money.' USD'.PHP_EOL;

    结果如下:

    2017-12-17 19475.80 19140.80 buy 5.0604000119016E-5 BTC for 0.977077215498 USD
    2018-01-17 11431.10 11188.60 buy 0.021132865240077 BTC for 239.00953593549 USD
    2018-02-17 10207.50 11112.70 buy 0.023787539157875 BTC for 253.57754617687 USD
    2018-03-17 8321.91 7916.88 buy 0.041003859231437 BTC for 332.92652962444 USD
    2018-04-17 8071.66 7902.09 buy 0.042375837907192 BTC for 338.45052038501 USD
    2018-05-17 8370.05 8094.32 buy 0.039887960431049 BTC for 328.36506954108 USD
    2018-06-17 6545.53 6499.27 buy 0.063541463744209 BTC for 414.44284312523 USD
    2018-07-17 6739.65 7321.04 buy 0.054691369482636 BTC for 384.4991959854 USD
    2018-08-17 6340.91 6580.63 buy 0.064759503785061 BTC for 418.39625926941 USD
    2018-09-17 6514.06 6281.20 buy 0.066044070848885 BTC for 422.52552898495 USD
    2018-10-17 6590.52 6544.43 buy 0.062672239975984 BTC for 411.59836923627 USD
    2018-11-17 5578.58 5554.33 buy 0.087239801476468 BTC for 485.61642912769 USD
    2018-12-17 3253.12 3545.86 buy 0.23390771030276 BTC for 795.16692209714 USD
    2019-01-17 3651.87 3678.56 buy 0.20122093521853 BTC for 737.51799007698 USD
    2019-02-17 3633.36 3673.84 buy 0.20250235419038 BTC for 739.86260126998 USD
    2019-03-17 4047.72 4025.23 buy 0.16590808085281 BTC for 669.68382066035 USD
    2019-04-17 5236.14 5251.94 buy 0.098296958985144 BTC for 515.47318479645 USD
    2019-05-17 7886.93 7343.90 buy 0.046610624688886 BTC for 354.95925041511 USD
    2019-06-17 8988.92 9320.35 buy 0.032254477185794 BTC for 295.27796575177 USD
    2019-07-17 9471.21 9693.80 buy 0.029438383903566 BTC for 282.09346094784 USD
    2019-08-17 10358.72 10231.74 buy 0.025503510657795 BTC for 262.56450802945 USD
    2019-09-17 10281.51 10241.27 buy 0.025671998916176 BTC for 263.43039295846 USD
    2019-10-17 8047.81 8103.91 buy 0.041447134508633 BTC for 334.72125569289 USD
    total:
    buy 1.6699492846915 BTC for 9281.1362573038 USD

    最后结果是花了9281.14USD(66293.85人民币)买到了1.6699492846915个比特币,这些比特币今天(2019-11-08)的价格是107,479.85元人民币。1.62倍的收益。

    所以定投策略还是有意义的,它的作用就在于你不用去判断现在是什么点位,能不能进去投,只要你判断这个标的长期是增长的,你随时可以开始定投,几乎可以保证你是盈利的。

  • 2019-11-07-定投比特币收益分析

    有一个朋友,我们认识很多年了。2013年的时候,记得某一天我跟他说比特币你可以了解一下,现在好像涨到一千块一个了,你可以花一万块钱买上十个,然后放起来,过几年再看,肯定不是现在的价格。他一脸蒙逼,然后事情就过去了。

    到了今年的某一天,那个朋友跟我说,现在比特币好几万一个了,当初应该买一点的,我说你现在买也不迟呀。然后他就决定以后每个月给我一千块钱让我帮他买比特币。

    今天我心血来潮就想算一下,如果从2013年起每个月买一千块钱比特币,现在有多少收益。(你看,为了朋友感觉扎心,我也很拼的。)

    首先要拿到比特币价格的历史数据,google一下之后,我在coinmarketcap找到从2013年04月28日到今天的数据。
    直接chrome里面右键copy element,把数据复制到一个history.html文件里面。

    数据大概长这样:

    <table class="table">
    <thead>
    <tr>
    <th class="text-left">Date</th>
    <th class="text-right">Open*</th>
    <th class="text-right">High</th>
    <th class="text-right">Low</th>
    <th class="text-right">Close**</th>
    <th class="text-right">Volume</th>
    <th class="text-right">Market Cap</th>
    </tr>
    </thead>
    <tbody>
    <tr class="text-right">
    <td class="text-left">Nov 06, 2019</td>
    <td data-format-fiat="" data-format-value="9340.86427973">9,340.86</td>
    <td data-format-fiat="" data-format-value="9423.23703723">9,423.24</td>
    <td data-format-fiat="" data-format-value="9305.90931362">9,305.91</td>
    <td data-format-fiat="" data-format-value="9360.87991187">9,360.88</td>
    <td data-format-market-cap="" data-format-value="23133895764.6">23,133,895,765</td>
    .
    ..
    ...
    <tr class="text-right">
    <td class="text-left">Apr 28, 2013</td>
    <td data-format-fiat="" data-format-value="135.300003052">135.30</td>
    <td data-format-fiat="" data-format-value="135.979995728">135.98</td>
    <td data-format-fiat="" data-format-value="132.100006104">132.10</td>
    <td data-format-fiat="" data-format-value="134.210006714">134.21</td>
    <td data-format-market-cap="" data-format-value="-">-</td>
    <td data-format-market-cap="" data-format-value="1488566728.25">1,488,566,728</td>
    </tr>
    </tbody>
    </table>

    一看这个数据很归整,每一行7个数据,分别是日期,开盘价格,最高价格,最低价格,收盘价格,交易量和总市值。我就写了个php脚本来分析:

    <?php
    $html = file_get_contents("history.html");
    $doc = new DOMDocument();
    $doc->loadHTML('<?xml encoding="utf-8" ?>'.$html);
    $trs = $doc->getElementsByTagName('tr');
    $ths = $trs[0]->getElementsByTagName('th');
    $keys = [];
    foreach ($ths as $th) {
        $keys[] = trim($th->nodeValue, "*").PHP_EOL;
    }
    $result = [];
    foreach ($trs as $tr) {
        $tds = $tr->getElementsByTagName('td');
        $row = [];
        if ($tds->length > 0) {
            for ($i = 0; $i<sizeof($keys); $i++) {
                if ($i == 0) {
                    $tds[$i]->nodeValue = date('Y-m-d', strtotime($tds[$i]->nodeValue));
                }
                $row[$keys[$i]] = str_replace(',', '', $tds[$i]->nodeValue);
            }
        }
        if ($row) {
            $result[]=$row;
        }
    }
    // reverse order
    $result = array_reverse($result);

    然后我就计算如果每个月一号买1000块钱的比特币,最后能买到多少币:

    (因为拿到的价格是用美元计算的,为了方便计算,我就把1000块写成140usd)

    $btc = 0;
    $money = 0;
    echo 'simple policy: buy 100 usd on the first day of everymonth:'.PHP_EOL;
    foreach ($result as $row) {
        if (explode("-", $row[$keys[0]])[2]=='01') {
            $price = ($row[$keys[1]]+$row[$keys[4]])/2;
            $btc += 140/$price;
            $money += 140;
            echo $row[$keys[0]].' '.$row[$keys[1]].' '.$row[$keys[4]].' ';
            echo 'buy '.(100/$price).' BTC for 100 USD'.PHP_EOL;
        }
    }
    echo 'total:'.PHP_EOL;
    echo 'buy '.$btc.' BTC for '.$money.' USD'.PHP_EOL;

    输出结果是这样的:

    buy 140 usd on the first day of everymonth:
    2013-05-01 139.00 116.99 buy 1.0937927262784 BTC for 140 USD
    2013-06-01 128.82 129.30 buy 1.0847667751433 BTC for 140 USD
    2013-07-01 97.51 88.05 buy 1.5089458935115 BTC for 140 USD
    2013-08-01 106.21 104.00 buy 1.3320013320013 BTC for 140 USD
    2013-09-01 135.14 138.34 buy 1.0238408658768 BTC for 140 USD
    2013-10-01 132.68 132.18 buy 1.0571622744091 BTC for 140 USD
    2013-11-01 203.90 206.18 buy 0.68279360124854 BTC for 140 USD
    2013-12-01 1128.92 955.85 buy 0.13430738162963 BTC for 140 USD
    2014-01-01 754.97 771.40 buy 0.18344176051678 BTC for 140 USD
    2014-02-01 828.61 832.58 buy 0.16855386801028 BTC for 140 USD
    2014-03-01 549.92 565.61 buy 0.25100176597671 BTC for 140 USD
    2014-04-01 457.00 478.38 buy 0.29934358228741 BTC for 140 USD
    2014-05-01 447.63 457.76 buy 0.30925899336198 BTC for 140 USD
    2014-06-01 623.69 630.23 buy 0.22329973204032 BTC for 140 USD
    2014-07-01 641.39 640.81 buy 0.21837466853845 BTC for 140 USD
    2014-08-01 586.20 594.92 buy 0.23706312652398 BTC for 140 USD
    2014-09-01 477.79 474.88 buy 0.29391079807278 BTC for 140 USD
    2014-10-01 387.43 383.61 buy 0.36314588088815 BTC for 140 USD
    2014-11-01 338.65 325.75 buy 0.421432871764 BTC for 140 USD
    2014-12-01 378.25 379.24 buy 0.36964184345668 BTC for 140 USD
    2015-01-01 320.43 314.25 buy 0.4411672023697 BTC for 140 USD
    2015-02-01 216.87 226.97 buy 0.6308579668349 BTC for 140 USD
    2015-03-01 254.28 260.20 buy 0.54423884310372 BTC for 140 USD
    2015-04-01 244.22 247.27 buy 0.56969622983174 BTC for 140 USD
    2015-05-01 235.94 232.08 buy 0.59826503140891 BTC for 140 USD
    2015-06-01 230.23 222.93 buy 0.61788330832377 BTC for 140 USD
    2015-07-01 263.35 258.62 buy 0.53642929670288 BTC for 140 USD
    2015-08-01 284.69 281.60 buy 0.49444630842854 BTC for 140 USD
    2015-09-01 230.26 228.12 buy 0.61084689558881 BTC for 140 USD
    2015-10-01 236.00 237.55 buy 0.59127864005913 BTC for 140 USD
    2015-11-01 315.01 325.43 buy 0.43719942539504 BTC for 140 USD
    2015-12-01 377.41 362.49 buy 0.37842951750237 BTC for 140 USD
    2016-01-01 430.72 434.33 buy 0.32368071209757 BTC for 140 USD
    2016-02-01 369.35 373.06 buy 0.37715009226708 BTC for 140 USD
    2016-03-01 437.92 435.12 buy 0.32071840923669 BTC for 140 USD
    2016-04-01 416.76 417.96 buy 0.33544182480353 BTC for 140 USD
    2016-05-01 448.48 451.88 buy 0.31098671642454 BTC for 140 USD
    2016-06-01 531.11 536.92 buy 0.26216492046104 BTC for 140 USD
    2016-07-01 672.52 676.30 buy 0.20758885544402 BTC for 140 USD
    2016-08-01 624.60 606.27 buy 0.22748137496242 BTC for 140 USD
    2016-09-01 575.55 572.30 buy 0.24393431197456 BTC for 140 USD
    2016-10-01 609.93 613.98 buy 0.2287749916252 BTC for 140 USD
    2016-11-01 701.34 729.79 buy 0.19564959158148 BTC for 140 USD
    2016-12-01 746.05 756.77 buy 0.18631639184999 BTC for 140 USD
    2017-01-01 963.66 998.33 buy 0.14271224623979 BTC for 140 USD
    2017-02-01 970.94 989.02 buy 0.14286005836854 BTC for 140 USD
    2017-03-01 1180.04 1222.50 buy 0.11654332498106 BTC for 140 USD
    2017-04-01 1071.71 1080.50 buy 0.1300988286459 BTC for 140 USD
    2017-05-01 1348.30 1421.60 buy 0.10108668182967 BTC for 140 USD
    2017-06-01 2288.33 2407.88 buy 0.059622546691907 BTC for 140 USD
    2017-07-01 2492.60 2434.55 buy 0.056827983722842 BTC for 140 USD
    2017-08-01 2871.30 2718.26 buy 0.050093388388353 BTC for 140 USD
    2017-09-01 4701.76 4892.01 buy 0.029185606909484 BTC for 140 USD
    2017-10-01 4341.05 4403.74 buy 0.032019065066171 BTC for 140 USD
    2017-11-01 6440.97 6767.31 buy 0.021198823768121 BTC for 140 USD
    2017-12-01 10198.60 10975.60 buy 0.013223640090299 BTC for 140 USD
    2018-01-01 14112.20 13657.20 buy 0.010083041045179 BTC for 140 USD
    2018-02-01 10237.30 9170.54 buy 0.014427159333548 BTC for 140 USD
    2018-03-01 10385.00 10951.00 buy 0.013123359580052 BTC for 140 USD
    2018-04-01 7003.06 6844.23 buy 0.020220563012691 BTC for 140 USD
    2018-05-01 9251.47 9119.01 buy 0.015241844524476 BTC for 140 USD
    2018-06-01 7500.70 7541.45 buy 0.018614360314184 BTC for 140 USD
    2018-07-01 6411.68 6385.82 buy 0.021879273295566 BTC for 140 USD
    2018-08-01 7769.04 7624.91 buy 0.018188963846186 BTC for 140 USD
    2018-09-01 7044.81 7193.25 buy 0.019665600510182 BTC for 140 USD
    2018-10-01 6619.85 6589.62 buy 0.02119691403213 BTC for 140 USD
    2018-11-01 6318.14 6377.78 buy 0.022054329264835 BTC for 140 USD
    2018-12-01 4024.46 4214.67 buy 0.033984170658795 BTC for 140 USD
    2019-01-01 3746.71 3843.52 buy 0.036889527721821 BTC for 140 USD
    2019-02-01 3460.55 3487.95 buy 0.040296466863352 BTC for 140 USD
    2019-03-01 3853.76 3859.58 buy 0.036300746498923 BTC for 140 USD
    2019-04-01 4105.36 4158.18 buy 0.033883783463262 BTC for 140 USD
    2019-05-01 5350.91 5402.70 buy 0.026037767782168 BTC for 140 USD
    2019-06-01 8573.84 8564.02 buy 0.016338095888285 BTC for 140 USD
    2019-07-01 10796.93 10583.13 buy 0.013096314977601 BTC for 140 USD
    2019-08-01 10077.44 10399.67 buy 0.013673804555428 BTC for 140 USD
    2019-09-01 9630.59 9757.97 buy 0.014441505712647 BTC for 140 USD
    2019-10-01 8299.72 8343.28 buy 0.016823889923692 BTC for 140 USD
    2019-11-01 9193.99 9261.10 buy 0.015171966107995 BTC for 140 USD
    total:
    buy 22.313812243429 BTC for 11060 USD

    就是从2013年5月1号起每个月的1号买1000块钱比特币,到2019年11月1号,总共79个月,共花了79000块钱,买到了22.313812243429个比特币,这些比特币今天(2019-11-07)的价格是1,429,726.68人民币。18.1倍的收益。

    我想起之前读过的一本书叫做价值平均策略,大概的意思是动态的调整每个月投资的额度,具体算法我不太记得了,我就自己想了一个简单粗暴的动态策略,用已知的最高价格作为参考值,把最高价格除以当前价格做为系数,这样价格越低买入的越多。代码如下:

    $btc = 0;
    $money = 0;
    $init_price = ($result[0][$keys[1]]+$result[0][$keys[4]])/2;
    echo 'initial price on '.$result[0][$keys[0]].': '.$init_price.PHP_EOL;
    foreach ($result as $row) {
        if (explode("-", $row[$keys[0]])[2]=='01') {
            $price = ($row[$keys[1]]+$row[$keys[4]])/2;
            $spend = 140*($init_price/$price);
            $btc += $spend/$price;
            $money += $spend;
            echo $row[$keys[0]].' '.$row[$keys[1]].' '.$row[$keys[4]].' ';
            echo 'buy '.$spend/$price.' BTC for '.$spend.' USD'.PHP_EOL;
            $init_price = $price > $init_price ? $price: $init_price;
        }
    }
    echo 'total:'.PHP_EOL;
    echo 'buy '.$btc.' BTC for '.$money.' USD'.PHP_EOL;

    输出结果是这样的:

    buy 140*(local high price/current price):
    initial price on 2013-04-28: 134.755
    2013-05-01 139.00 116.99 buy 1.1515609112047 BTC for 147.39403882964 USD
    2013-06-01 128.82 129.30 buy 1.1326340212648 BTC for 146.17774678444 USD
    2013-07-01 97.51 88.05 buy 2.191614613927 BTC for 203.33800388015 USD
    2013-08-01 106.21 104.00 buy 1.7077573806559 BTC for 179.49383949384 USD
    2013-09-01 135.14 138.34 buy 1.0089781766947 BTC for 137.96767588123 USD
    2013-10-01 132.68 132.18 buy 1.0915681447006 BTC for 144.5563694027 USD
    2013-11-01 203.90 206.18 buy 0.45535113653299 BTC for 93.365197034725 USD
    2013-12-01 1128.92 955.85 buy 0.026418631819663 BTC for 27.538385529339 USD
    2014-01-01 754.97 771.40 buy 0.25055122877977 BTC for 191.21693953629 USD
    2014-02-01 828.61 832.58 buy 0.21153272498137 BTC for 175.6980237059 USD
    2014-03-01 549.92 565.61 buy 0.46908729631231 BTC for 261.64047582763 USD
    2014-04-01 457.00 478.38 buy 0.667175394006 BTC for 312.03126002266 USD
    2014-05-01 447.63 457.76 buy 0.71210624326671 BTC for 322.36693579562 USD
    2014-06-01 623.69 630.23 buy 0.37125859892633 BTC for 232.76429118285 USD
    2014-07-01 641.39 640.81 buy 0.35506235979481 BTC for 227.63047886445 USD
    2014-08-01 586.20 594.92 buy 0.41843512452875 BTC for 247.1110471417 USD
    2014-09-01 477.79 474.88 buy 0.6431780306908 BTC for 306.3682072491 USD
    2014-10-01 387.43 383.61 buy 0.98188892677318 BTC for 378.5378190496 USD
    2014-11-01 338.65 325.75 buy 1.3223820109383 BTC for 439.29530403371 USD
    2014-12-01 378.25 379.24 buy 1.0173312201919 BTC for 385.30911299159 USD
    2015-01-01 320.43 314.25 buy 1.4491273531296 BTC for 459.86607424214 USD
    2015-02-01 216.87 226.97 buy 2.9632159415969 BTC for 657.59688175919 USD
    2015-03-01 254.28 260.20 buy 2.2053584452988 BTC for 567.30640646867 USD
    2015-04-01 244.22 247.27 buy 2.416500048966 BTC for 593.84280453315 USD
    2015-05-01 235.94 232.08 buy 2.6649395101285 BTC for 623.62249476518 USD
    2015-06-01 230.23 222.93 buy 2.8425822771077 BTC for 644.07229234707 USD
    2015-07-01 263.35 258.62 buy 2.1425210354757 BTC for 559.16585244363 USD
    2015-08-01 284.69 281.60 buy 1.8202808285906 BTC for 515.40341521129 USD
    2015-09-01 230.26 228.12 buy 2.7782086533372 BTC for 636.73764125834 USD
    2015-10-01 236.00 237.55 buy 2.6030619162413 BTC for 616.33998521803 USD
    2015-11-01 315.01 325.43 buy 1.4231781994891 BTC for 455.73012304041 USD
    2015-12-01 377.41 362.49 buy 1.0662772066542 BTC for 394.4692526017 USD
    2016-01-01 430.72 434.33 buy 0.78007032906727 BTC for 337.39991907982 USD
    2016-02-01 369.35 373.06 buy 1.0590794814936 BTC for 393.13559892782 USD
    2016-03-01 437.92 435.12 buy 0.76585736967879 BTC for 334.31205901219 USD
    2016-04-01 416.76 417.96 buy 0.83778878317957 BTC for 349.65952654782 USD
    2016-05-01 448.48 451.88 buy 0.72008505131324 BTC for 324.1678884002 USD
    2016-06-01 531.11 536.92 buy 0.51173989609801 BTC for 273.27678061478 USD
    2016-07-01 672.52 676.30 buy 0.32085453816226 BTC for 216.38750908201 USD
    2016-08-01 624.60 606.27 buy 0.385293610276 BTC for 237.12317304021 USD
    2016-09-01 575.55 572.30 buy 0.44304302441539 BTC for 254.2734677876 USD
    2016-10-01 609.93 613.98 buy 0.38968816276562 BTC for 238.47161964524 USD
    2016-11-01 701.34 729.79 buy 0.28500862887461 BTC for 203.94219952066 USD
    2016-12-01 746.05 756.77 buy 0.25846530139145 BTC for 194.21341211855 USD
    2017-01-01 963.66 998.33 buy 0.15164308156174 BTC for 148.76110479666 USD
    2017-02-01 970.94 989.02 buy 0.15195736845904 BTC for 148.91518194249 USD
    2017-03-01 1180.04 1222.50 buy 0.10112881684416 BTC for 121.48301381038 USD
    2017-04-01 1071.71 1080.50 buy 0.14523101359762 BTC for 156.28381988746 USD
    2017-05-01 1348.30 1421.60 buy 0.087679987206416 BTC for 121.43239828153 USD
    2017-06-01 2288.33 2407.88 buy 0.035166334572328 BTC for 82.574246040956 USD
    2017-07-01 2492.60 2434.55 buy 0.054164404460803 BTC for 133.43807271952 USD
    2017-08-01 2871.30 2718.26 buy 0.044156899397747 BTC for 123.40881929884 USD
    2017-09-01 4701.76 4892.01 buy 0.017004233055095 BTC for 81.567350478488 USD
    2017-10-01 4341.05 4403.74 buy 0.03512760693623 BTC for 153.59177292994 USD
    2017-11-01 6440.97 6767.31 buy 0.015397662640547 BTC for 101.68831975094 USD
    2017-12-01 10198.60 10975.60 buy 0.0082487905532152 BTC for 87.330770465944 USD
    2018-01-01 14112.20 13657.20 buy 0.0076883305976663 BTC for 106.75016384942 USD
    2018-02-01 10237.30 9170.54 buy 0.020642872076286 BTC for 200.31677919851 USD
    2018-03-01 10385.00 10951.00 buy 0.017080419081473 BTC for 182.21391076115 USD
    2018-04-01 7003.06 6844.23 buy 0.040550382242634 BTC for 280.7564512623 USD
    2018-05-01 9251.47 9119.01 buy 0.023040055422503 BTC for 211.628438669 USD
    2018-06-01 7500.70 7541.45 buy 0.034364078094468 BTC for 258.45480865435 USD
    2018-07-01 6411.68 6385.82 buy 0.04747601421011 BTC for 303.78714592694 USD
    2018-08-01 7769.04 7624.91 buy 0.032811371521298 BTC for 252.54830631514 USD
    2018-09-01 7044.81 7193.25 buy 0.038355079751556 BTC for 273.05096340372 USD
    2018-10-01 6619.85 6589.62 buy 0.044560878258085 BTC for 294.31279226192 USD
    2018-11-01 6318.14 6377.78 buy 0.048238764192504 BTC for 306.21774554345 USD
    2018-12-01 4024.46 4214.67 buy 0.11454122324716 BTC for 471.86001434617 USD
    2019-01-01 3746.71 3843.52 buy 0.13496297887131 BTC for 512.20002555917 USD
    2019-02-01 3460.55 3487.95 buy 0.16104320456432 BTC for 559.50435345758 USD
    2019-03-01 3853.76 3859.58 buy 0.1306891631676 BTC for 504.02497491359 USD
    2019-04-01 4105.36 4158.18 buy 0.11386552694181 BTC for 470.46616825235 USD
    2019-05-01 5350.91 5402.70 buy 0.067238182215101 BTC for 361.52659432507 USD
    2019-06-01 8573.84 8564.02 buy 0.02647349902264 BTC for 226.84955998007 USD
    2019-07-01 10796.93 10583.13 buy 0.017010093009046 BTC for 181.83840456949 USD
    2019-08-01 10077.44 10399.67 buy 0.0185433075381 BTC for 189.85667411075 USD
    2019-09-01 9630.59 9757.97 buy 0.020683947066558 BTC for 200.51597436839 USD
    2019-10-01 8299.72 8343.28 buy 0.028071220864445 BTC for 233.59466442348 USD
    2019-11-01 9193.99 9261.10 buy 0.022829278840654 BTC for 210.65819781968 USD
    total:
    buy 51.375763838804 BTC for 22893.725512278 USD

    最后结果是花了22893.72USD(就是163526.57人民币)买到了51.375763838804个比特币,这些比特币今天(2019-11-07)的价格是3,290,352.13元人民币。20.1倍的收益。

    两者‌‍‍‌‍​‌‌‍​‌‌‌‌‌​‌‌‌‍‍​‍‍‍‍‌​‌‌‍‌​‌‍‍‌‍​‌‌‍​‍‌‌‌‌​‌‌‌‌‌​‍‍‍‌‌​‍‍‌‌‌​‌‍‍‌‍​‌‌‍​‍‍‍‍‌​‌‌‌‌‍​‌‌‍‌​‌​‌‍‍‌‍​‌‌‍​‍‌‌‌‌​‌‌‌‍‍​‌‍​‌‌‌‌‌​​‌‌‌‌​‍​‍​‌‍‍‌​‌‌‌​​​​‍​‌​‍‌‍‌​‌‌‌‌​​‌‍‌‌​‌‍​‍‍‍​‍‍‌​‍‍‍​‍‌​‍‍‌​‌‌‌​‌‌‌‌​‌‌‍​‍‍‍​​‍‌‍‌​‍‍‍​‍‍​对比,后一个方案虽然收益多了两倍,但是投入的资金也翻倍了,而且每个月还要计算投多少钱进去,感觉不太值得,当然如果看最后结果的话,

    79000变成1,429,726.68 (八万变成一百四十万)

    163526.57变成3,290,352.13 (十六万变成三百二十万)

    可能很多人还是愿意选择第二个方案,因为16万虽然不是小数目,但是努力一下还是可以拿出来的,最后的收益也更有吸引力的。

    友情提示,本文章只是历史数据分析,并不构成任何投资建议,比特币风险很大,你需要强大的心脏来承受价格的巨大波动。

  • 2019-10-29-镜子

    今天看了一个宗萨蒋杨庆哲仁波切的视频,里面提到了一个很好的比喻,就是镜子。

    我们都知道镜子里面的自己只是自己的投影而已,并不是真实的,因此我们刮胡子时候只需要刮一遍,如果镜子里面的自己是真实的话,我们就要给他也刮一遍,同时生活中所有的一切都要双份儿。

    这个结论延伸到自己就没有那么容易理解了,佛陀说整个世界包括我们自己,其实是我们的心形成的投影,准确的说是我们的无明形成的投影。

    但是整个世界和自己的投影如此的逼真,甚至比镜子还要逼真,而且它没有镜子那样明显的边界,所以我们无法意识到我们自己和我们看到的感受到的整个世界都只是无明的投影。

    我不知道镜子里面的人是否有思想,如果有思想,不知道他们能不能够意识到自己只是投影。

    这个比方可能有点不恰当,我另外举个例子,比如我们假设游戏里面的人物突然有了思想,对他们来说他们的世界是很真实的,他们可能根本意识不到自己是虚幻的,是受我们控制的,他们也许会形成一套哲学,对自己的行为做出合理的解释,就像我们一样。

    脑洞再开大一点的话,我们无从得知我们是否本身就是某个高级智慧在某个超级电脑上模拟出来的宇宙里面的一个角色而已。

    那个高级智慧和那个超级电脑对我们来说是不可能理解的,就像游戏里面的人物不可能理解自己是受程序控制的,他们可能不理解什么是程序,就像我们不理解什么是无明一样,他们不可能理解什么是电脑,就像我们不能理解整个宇宙一样。

    我们回到佛教话题,佛教的本质就是,有一个智者叫做佛陀,他意识到了我们实际上是一个东西的投影。

    那个东西叫做无明,去除了无明之后,我们每个人都是超越了一切概念的存在,那个存在可以取名叫做佛性,可以取名叫做道,可以取名叫做上帝,但是这些名字并没有办法描述那个存在,因为那个存在是超越了一切概念包括时间和空间的,所以根本没有办法用我们的语言来描述。

    佛陀指点我们方法来达到那个不可描述的存在,这些方法就是八万四千法门,这些方法的本质是去除无明,去除无明就是涅盘或者叫做开悟,或者叫做见性,去除无明的同时我们就去除了所有的烦恼,因为所有的烦恼本质上也是因为无明。

    下一次照镜子的时候,你可以对镜子里的自己说,你不是真的,我也不是。