Showing preview only (994K chars total). Download the full file or copy to clipboard to get everything.
Repository: JuliaCN/julia_zh_cn
Branch: master
Commit: e9b94bc6daef
Files: 69
Total size: 831.5 KB
Directory structure:
gitextract_uvq9cdvn/
├── .gitignore
├── .gitmodules
├── DocCheck.jl
├── Makefile
├── README.md
├── VERSION
├── _templates/
│ └── sidebarintro.html
├── conf.py
├── devdocs/
│ ├── cartesian.rst
│ └── index.rst
├── index.rst
├── latex.rst
├── manual/
│ ├── arrays.rst
│ ├── calling-c-and-fortran-code.rst
│ ├── complex-and-rational-numbers.md
│ ├── complex-and-rational-numbers.rst
│ ├── constructors.rst
│ ├── control-flow.rst
│ ├── conversion-and-promotion.rst
│ ├── dates.rst
│ ├── embedding.rst
│ ├── faq.rst
│ ├── functions.rst
│ ├── getting-started.md
│ ├── getting-started.rst
│ ├── index.rst
│ ├── integers-and-floating-point-numbers.md
│ ├── integers-and-floating-point-numbers.rst
│ ├── interacting-with-julia.rst
│ ├── introduction.rst
│ ├── linear-algebra.md
│ ├── linear-algebra.rst
│ ├── mathematical-operations.rst
│ ├── metaprogramming.rst
│ ├── methods.rst
│ ├── modules.rst
│ ├── networking-and-streams.rst
│ ├── noteworthy-differences.rst
│ ├── nullable-types.rst
│ ├── packages.rst
│ ├── parallel-computing.rst
│ ├── performance-tips.rst
│ ├── running-external-programs.rst
│ ├── stacktraces.md
│ ├── strings.rst
│ ├── style-guide.md
│ ├── style-guide.rst
│ ├── types.rst
│ ├── variables-and-scoping.rst
│ ├── variables.md
│ └── variables.rst
├── note/
│ ├── index.rst
│ ├── macroes.rst
│ ├── optimize.rst
│ └── uses.rst
├── requirements.txt
└── stdlib/
├── base.rst
├── collections.rst
├── constants.rst
├── file.rst
├── graphics.rst
├── index.rst
├── linalg.rst
├── pkg.rst
├── profile.rst
├── punctuation.rst
├── sort.rst
├── sparse.rst
└── test.rst
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
_build/
================================================
FILE: .gitmodules
================================================
================================================
FILE: DocCheck.jl
================================================
# Julia utilities for checking documentation
# This file contains a number of functions for checking julia documentation
#
# isdeprecated(v) : test if v is deprecated
# isdocumented(v) : true if v is documented
# undefined_exports(m) : returns a list of undefined exports in module m
# undocumented(m) : returns a list of undocumented exports in module m
# undocumented_by_file(m) : returns a dictionary of undocumented exports,
# with file, function, and line number information
# undocumented_rst(m) : produce a list of undocumented function suitable for
# pasting into github issue #2242
module DocCheck
import Base.Help: init_help, FUNCTION_DICT, MODULE_DICT
import Base: argtype_decl, uncompressed_ast
export isdeprecated, isdocumented, undefined_exports, undocumented, undocumented_by_file, undocumented_rst,
gen_undocumented_template
isdeprecated(m::Module, v) = try endswith(functionloc(eval(m, v))[1], "deprecated.jl") catch return false end
isdeprecated(v) = try endswith(functionloc(eval(v))[1], "deprecated.jl") catch return false end
isdocumented(v) = (s=string(v); haskey(FUNCTION_DICT, s) || haskey(MODULE_DICT, s))
modfuncjoin(m::String, f::String) = beginswith(f, '@') ? "@$m.$(f[2:end])" : "$m.$f"
modfuncjoin(m, f) = modfuncjoin(string(m), string(f))
# return a list of undefined exports in a module
undefined_exports(m::Module) = sort(filter(x->!isdefined(x), names(m)))
undefined_exports() = undefined(Base)
# Check for exported names that aren't documented,
# and return a Dict with (fn::Symbol, fullname::String) pairs
function undocumented(m::Module)
init_help()
undoc = Dict{Symbol, Array}()
for v in sort(names(m))
if isdefined(m,v) && !isdocumented(v) && !isdeprecated(m,v)
ms = modfuncjoin(m,v)
haskey(undoc, v) ? push!(undoc[v], ms) : (undoc[v] = [ms])
end
end
undoc
end
undocumented() = undocumented(Base)
# Check for exported names that aren't documented, and
# return the file, function names, and line numbers, if available
function undocumented_by_file(m::Module)
init_help()
undocf = Dict{String, Dict}()
for (f,_) in undocumented(m)
s = string(f)
try
for (file, line) in functionlocs(eval(f))
if beginswith(file, JULIA_HOME)
file = replace(file, JULIA_HOME, "\$JULIA_HOME", 1)
end
if !haskey(undocf, file)
undocf[file] = Dict{String, Vector{Integer}}()
end
if !haskey(undocf[file], s)
undocf[file][s] = [line]
else
push!(undocf[file][s], line)
end
end
catch
if !haskey(undocf, "UNKNOWN_FILE")
undocf["UNKNOWN_FILE"] = Dict{String, Vector{Integer}}()
end
undocf["UNKNOWN_FILE"][s] = Integer[-1]
end
end
undocf
end
undocumented_by_file() = undocumented_by_file(Base)
# Unlike the above functions, this version parses base/exports.jl,
# because that file groups the functions in a more systematic manner.
# The output can be pasted into https://github.com/JuliaLang/julia/issues/2242
# This also only works with Base functions; the other "undocumented*"
# functions are more general.
# Based on code by @jihao
function _undocumented_rst()
init_help()
depdoc = havecount = total = 0
out = String["The following exports are not documented:"]
undoc_exports = Set()
exports=[strip(x) for x in split(replace(open(readall, "$JULIA_HOME/../../base/exports.jl"),",",""),"\n")]
for line in exports
if search(line, "deprecated")!=0:-1; continue end
if haskey(MODULE_DICT, line); havecount+=1; total+=1; continue end
if length(line)>1
if line[1]=='#'
if line[2]!= ' ' continue end
else
s = symbol(line) # for submodules: string(:Sort) == "Base.Sort"
if !isdefined(s) continue end
if haskey(FUNCTION_DICT, line) || haskey(MODULE_DICT, line)
m = eval(symbol(getkey(MODULE_DICT, line, "Base")))
isdeprecated(m,s) && continue
havecount+=1; total+=1; continue
end
push!(undoc_exports, line)
if line[1]=='@'; line = line[2:end] end
line=string("- [ ] ", line)
total+=1
end
end
push!(out, line)
end
append!(out, String["", "Documented and deprecated functions/exports (please update docs)", ""])
deprecated=[strip(x) for x in split(replace(open(readall, "$JULIA_HOME/../../base/deprecated.jl"),",",""),"\n")]
for line in deprecated
if beginswith(line, "@deprecated")
fn = split(line, r" +")[2]
if haskey(MODULE_DICT, fn); push!(out, string("- [ ] ", fn)); depdoc += 1 end
elseif beginswith(line, "export")
for fn in split(line, r"[ ,]+")[2:end]
if haskey(MODULE_DICT, fn); push!(out, string("- [ ]", fn)); depdoc += 1 end
end
end
end
prepend!(out, String["$havecount/$total exports have been documented",
"(Additionally, $depdoc deprecated functions are still documentated)",
""])
(join(out, "\n"), undoc_exports)
end
undocumented_rst() = println(_undocumented_rst()[1])
function gen_undocumented_template(outfile = "$JULIA_HOME/../../doc/UNDOCUMENTED.rst")
out = open(outfile, "w")
init_help()
println(out, ".. currentmodule:: Base")
println(out)
exports=[strip(x) for x in split(replace(open(readall, "$JULIA_HOME/../../base/exports.jl"),",",""),"\n")]
for line in exports
if search(line, "deprecated")!=0:-1; continue end
if haskey(MODULE_DICT, line); continue end
if length(line)>1
if line[1]=='#'
if line[2]!= ' ' continue end
println(out)
println(out, line[3:end])
println(out, repeat("-", length(line)-2))
println(out)
continue
else
s = symbol(line) # for submodules: string(:Sort) == "Base.Sort"
if !isdefined(s) continue end
if haskey(FUNCTION_DICT, line) || haskey(MODULE_DICT, line)
continue
end
if line[1]=='@'; line = line[2:end] end
sym = try eval(symbol(line)) catch :() end
if isa(sym, Function)
mt = methods(sym)
if length(mt) == 1 # easy case
m = mt.defs
li = m.func.code
e = uncompressed_ast(li)
argnames = e.args[1]
decls = map(argtype_decl, argnames, {m.sig...})
args = join(decls, ",")
line = line * "($args)"
else
line = line * "(...)"
end
println(out, ".. function:: "*line)
println(out)
println(out, " UNDOCUMENTED")
println(out)
elseif isa(sym, Module)
println(out, ".. module:: "*line)
println(out)
println(out, " UNDOCUMENTED (may not appear in helpdb.jl)")
println(out)
end
end
end
end
close(out)
nothing
end
end
================================================
FILE: Makefile
================================================
# Makefile for Sphinx documentation
#
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = PYTHONPATH=$(PYTHONPATH):juliadoc sphinx-build
PAPER =
BUILDDIR = _build
JULIA = ../julia
# Internal variables.
PAPEROPT_a4 = -D latex_paper_size=a4
PAPEROPT_letter = -D latex_paper_size=letter
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
# the i18n builder cannot share the environment and doctrees with the others
I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
.PHONY: help clean cleanall html dirhtml singlehtml pickle json htmlhelp qthelp devhelp \
epub latex latexpdf text man changes linkcheck doctest gettext
help:
@echo "Please use \`make <target>' where <target> is one of"
@echo " helpdb.jl to make the REPL help db"
@echo " html to make standalone HTML files"
@echo " dirhtml to make HTML files named index.html in directories"
@echo " singlehtml to make a single large HTML file"
@echo " pickle to make pickle files"
@echo " json to make JSON files"
@echo " htmlhelp to make HTML files and a HTML help project"
@echo " qthelp to make HTML files and a qthelp project"
@echo " devhelp to make HTML files and a Devhelp project"
@echo " epub to make an epub"
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
@echo " latexpdf to make LaTeX files and run them through pdflatex"
@echo " text to make text files"
@echo " man to make manual pages"
@echo " texinfo to make Texinfo files"
@echo " info to make Texinfo files and run them through makeinfo"
@echo " gettext to make PO message catalogs"
@echo " changes to make an overview of all changed/added/deprecated items"
@echo " linkcheck to check all external links for integrity"
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
clean:
-rm -rf $(BUILDDIR)/*
html:
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
dirhtml:
$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
singlehtml:
$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
@echo
@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
pickle:
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
@echo
@echo "Build finished; now you can process the pickle files."
json:
$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
@echo
@echo "Build finished; now you can process the JSON files."
htmlhelp:
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
@echo
@echo "Build finished; now you can run HTML Help Workshop with the" \
".hhp project file in $(BUILDDIR)/htmlhelp."
qthelp:
$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
@echo
@echo "Build finished; now you can run "qcollectiongenerator" with the" \
".qhcp project file in $(BUILDDIR)/qthelp, like this:"
@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/JuliaLanguage.qhcp"
@echo "To view the help file:"
@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/JuliaLanguage.qhc"
devhelp:
$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
@echo
@echo "Build finished."
@echo "To view the help file:"
@echo "# mkdir -p $$HOME/.local/share/devhelp/JuliaLanguage"
@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/JuliaLanguage"
@echo "# devhelp"
epub:
$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
@echo
@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
latex:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo
@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
@echo "Run \`make' in that directory to run these through (pdf)latex" \
"(use \`make latexpdf' here to do that automatically)."
latexpdf:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo "Running LaTeX files through pdflatex..."
$(MAKE) -C $(BUILDDIR)/latex all-pdf
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
text:
$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
@echo
@echo "Build finished. The text files are in $(BUILDDIR)/text."
man:
$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
@echo
@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
texinfo:
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
@echo
@echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
@echo "Run \`make' in that directory to run these through makeinfo" \
"(use \`make info' here to do that automatically)."
info:
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
@echo "Running Texinfo files through makeinfo..."
make -C $(BUILDDIR)/texinfo info
@echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
gettext:
$(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
@echo
@echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
changes:
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
@echo
@echo "The overview file is in $(BUILDDIR)/changes."
linkcheck:
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
@echo
@echo "Link check complete; look for any errors in the above output " \
"or in $(BUILDDIR)/linkcheck/output.txt."
doctest:
$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
@echo "Testing of doctests in the sources finished, look at the " \
"results in $(BUILDDIR)/doctest/output.txt."
helpdb.jl: stdlib/*.rst
python -c "import sphinx,sys; sys.exit(not sphinx.__version__ < '1.2')"
$(SPHINXBUILD) -b jlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/jlhelp
mv $(BUILDDIR)/jlhelp/jlhelp.jl helpdb_zh_CN.jl
================================================
FILE: README.md
================================================
# Julia中文文档 0.3 版本翻译
这是旧版 Julia 中文文档,新版Julia文档请查看 [JuliaZH.jl](https://github.com/JuliaCN/JuliaZH.jl)
================================================
FILE: VERSION
================================================
0.3.0-prerelease
================================================
FILE: _templates/sidebarintro.html
================================================
================================================
FILE: conf.py
================================================
# -*- coding: utf-8 -*-
#
# Julia Language documentation build configuration file, created by
# sphinx-quickstart on Sat Apr 14 22:49:22 2012.
#
# This file is execfile()d with the current directory set to its containing dir.
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.
import sys, os, re
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
import juliadoc
import sphinx_rtd_theme
# -- General configuration -----------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
#needs_sphinx = '1.0'
# Add any Sphinx extension module names here, as strings. They can be extensions
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = ['sphinx.ext.mathjax',
'juliadoc.julia',
'juliadoc.jldoctest',
'juliadoc.jlhelp']
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# The suffix of source filenames.
source_suffix = '.rst'
# The encoding of source files.
#source_encoding = 'utf-8-sig'
# The master toctree document.
master_doc = 'index'
# General information about the project.
project = u'Julia Language'
AUTHORS = u"作者 JuliaLang 译者 JuliaCN"
copyright = u'2012-2014, '+AUTHORS
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
try:
# The full version, including alpha/beta/rc tags.
with open("./VERSION") as f:
release = f.read().rstrip()
# The short X.Y version.
version = '.'.join(re.split('[.-]', release)[:2])
except:
release = 'X.Y.Z-unknown'
version = 'X.Y'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
language = 'zh_CN'
# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
#today = ''
# Else, today_fmt is used as the format for a strftime call.
#today_fmt = '%B %d, %Y'
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
exclude_patterns = ['_build']
# The reST default role (used for this markup: `text`) to use for all documents.
#default_role = None
# If true, '()' will be appended to :func: etc. cross-reference text.
#add_function_parentheses = True
# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
add_module_names = False
# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
#show_authors = False
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
# A list of ignored prefixes for module index sorting.
#modindex_common_prefix = []
primary_domain = 'jl'
highlight_language = 'julia'
# -- Options for HTML output ---------------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
html_theme = 'julia'
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
#html_theme_options = {}
# Add any paths that contain custom themes here, relative to this directory.
html_theme_path = [juliadoc.get_theme_dir(),
sphinx_rtd_theme.get_html_theme_path()]
# The name for this set of Sphinx documents. If None, it defaults to
# "<project> v<release> documentation".
#html_title = None
# A shorter title for the navigation bar. Default is the same as html_title.
#html_short_title = None
# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
#html_logo = None
# The name of an image file (within the static path) to use as favicon of the
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
#html_favicon = None
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = []
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
#html_last_updated_fmt = '%b %d, %Y'
# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
#html_use_smartypants = True
# Custom sidebar templates, maps document names to template names.
html_sidebars = {}
# Additional templates that should be rendered to pages, maps page names to
# template names.
#html_additional_pages = {}
# If false, no module index is generated.
html_domain_indices = False
# If false, no index is generated.
#html_use_index = True
# If true, the index is split into individual pages for each letter.
#html_split_index = False
# If true, links to the reST sources are added to the pages.
#html_show_sourcelink = True
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
html_show_sphinx = False
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
html_show_copyright = False
# If true, an OpenSearch description file will be output, and all pages will
# contain a <link> tag referring to it. The value of this option must be the
# base URL from which the finished HTML is served.
#html_use_opensearch = ''
# This is the file name suffix for HTML files (e.g. ".xhtml").
#html_file_suffix = None
# Output file base name for HTML help builder.
htmlhelp_basename = 'JuliaLanguageDoc'
# -- Options for LaTeX output --------------------------------------------------
latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
#'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
#'preamble': '',
'preamble': r'''
\hypersetup{unicode=true}
\usepackage{CJKutf8}
\DeclareUnicodeCharacter{00A0}{\nobreakspace}
\DeclareUnicodeCharacter{2203}{\ensuremath{\exists}}
\DeclareUnicodeCharacter{2200}{\ensuremath{\forall}}
\DeclareUnicodeCharacter{2286}{\ensuremath{\subseteq}}
\DeclareUnicodeCharacter{2713}{x}
\DeclareUnicodeCharacter{27FA}{\ensuremath{\Longleftrightarrow}}
\DeclareUnicodeCharacter{221A}{\ensuremath{\sqrt{}}}
\DeclareUnicodeCharacter{221B}{\ensuremath{\sqrt[3]{}}}
\DeclareUnicodeCharacter{2295}{\ensuremath{\oplus}}
\DeclareUnicodeCharacter{2297}{\ensuremath{\otimes}}
\begin{CJK}{UTF8}{gbsn}
\AtEndDocument{\end{CJK}}
''',
}
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, documentclass [howto/manual]).
latex_documents = [
('latex', 'JuliaLanguage.tex', u'Julia 文档',
AUTHORS, 'manual'),
]
# The name of an image file (relative to this directory) to place at the top of
# the title page.
#latex_logo = None
# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
latex_use_parts = True
# If true, show page references after internal links.
#latex_show_pagerefs = False
# If true, show URL addresses after external links.
#latex_show_urls = False
# Documents to append as an appendix to all manuals.
#latex_appendices = []
# If false, no module index is generated.
latex_domain_indices = False
# -- Options for manual page output --------------------------------------------
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
('index', 'julialanguage', u'Julia 文档',
[AUTHORS], 1)
]
# If true, show URL addresses after external links.
#man_show_urls = False
# -- Options for Texinfo output ------------------------------------------------
# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
('index', 'JuliaLanguage', u'Julia 文档',
AUTHORS, 'JuliaLanguage', 'One line description of project.',
'Miscellaneous'),
]
# Documents to append as an appendix to all manuals.
#texinfo_appendices = []
# If false, no module index is generated.
#texinfo_domain_indices = True
# How to display URL addresses: 'footnote', 'no', or 'inline'.
#texinfo_show_urls = 'footnote'
================================================
FILE: devdocs/cartesian.rst
================================================
.. module:: Base.Cartesian
.. _devdocs-cartesian:
Base.Cartesian
==============
The (non-exported) Cartesian module provides macros that facilitate
writing multidimensional algorithms. It is hoped that Cartesian will
not, in the long term, be necessary; however, at present it is one of
the few ways to write compact and performant multidimensional code.
Principles of usage
-------------------
A simple example of usage is::
@nloops 3 i A begin
s += @nref 3 A i
end
which generates the following code::
for i_3 = 1:size(A,3)
for i_2 = 1:size(A,2)
for i_1 = 1:size(A,1)
s += A[i_1,i_2,i_3]
end
end
end
In general, Cartesian allows you to write generic code that contains
repetitive elements, like the nested loops in this example. Other
applications include repeated expressions (e.g., loop unwinding) or
creating function calls with variable numbers of arguments without using
the "splat" construct (``i...``).
Basic syntax
------------
The (basic) syntax of ``@nloops`` is as follows:
- The first argument must be an integer (*not* a variable) specifying
the number of loops.
- The second argument is the symbol-prefix used for the iterator
variable. Here we used ``i``, and variables ``i_1, i_2, i_3`` were
generated.
- The third argument specifies the range for each iterator variable. If
you use a variable (symbol) here, it's taken as ``1:size(A,dim)``.
More flexibly, you can use the anonymous-function expression syntax
described below.
- The last argument is the body of the loop. Here, that's what appears
between the ``begin...end``.
There are some additional features of ``@nloops`` described in the
:ref:`reference section <devdoc-cartesian-reference>`.
``@nref`` follows a similar pattern, generating ``A[i_1,i_2,i_3]`` from
``@nref 3 A i``. The general practice is to read from left to right,
which is why ``@nloops`` is ``@nloops 3 i A expr`` (as in
``for i_2 = 1:size(A,2)``, where ``i_2`` is to the left and the range is
to the right) whereas ``@nref`` is ``@nref 3 A i`` (as in
``A[i_1,i_2,i_3]``, where the array comes first).
If you're developing code with Cartesian, you may find that debugging is
easier when you examine the generated code, using ``macroexpand``::
julia> macroexpand(:(@nref 2 A i))
:(A[i_1,i_2])
Supplying the number of expressions
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The first argument to both of these macros is the number of
expressions, which must be an integer. When you're writing a function
that you intend to work in multiple dimensions, this may not be
something you want to hard-code. Perhaps the most straightforward
approach is to use the ``@ngenerate`` macro.
Perhaps the easiest way to understand ``@ngenerate`` is to see it in
action. Here's a slightly cleaned up example::
julia> macroexpand(:(@ngenerate N typeof(A) function mysum{T,N}(A::Array{T,N})
s = zero(T)
@nloops N i A begin
s += @nref N A i
end
s
end))
:(begin
function mysum{T}(A::Array{T,1}) # none, line 2:
s = zero(T) # line 3:
for i_1 = 1:size(A,1) # line 293:
s += A[i_1]
end # line 295:
s
end
function mysum{T}(A::Array{T,2}) # none, line 2:
s = zero(T) # line 3:
for i_2 = 1:size(A,2) # line 293:
for i_1 = 1:size(A,1) # line 293:
s += A[i_1,i_2]
end # line 295:
end # line 295:
s
end
function mysum{T}(A::Array{T,3}) # none, line 2:
s = zero(T) # line 3:
for i_3 = 1:size(A,3) # line 293:
for i_2 = 1:size(A,2) # line 293:
for i_1 = 1:size(A,1) # line 293:
s += A[i_1,i_2,i_3]
end # line 295:
end # line 295:
end # line 295:
s
end
function mysum{T}(A::Array{T,4}) # none, line 2:
...
end
let mysum_cache = Dict{Int,Function}() # line 113:
function mysum{T,N}(A::Array{T,N}) # cartesian.jl, line 100:
if !(haskey(mysum_cache,N)) # line 102:
localfunc = quote
function _F_{T}(A::Array{T,$N})
s = zero(T)
@nloops $N i A begin
s += @nref $N A i
end
s
end
end
mysum_cache[N] = eval(quote
local _F_
$localfunc
_F_
end)
end
mysum_cache[N](A)::typeof(A)
end
end
end)
You can see that ``@ngenerate`` causes explicit versions to be
generated for dimensions 1 to 4 (a setting controlled by the constant
``CARTESIAN_DIMS``). To allow arbitrary-dimensional arrays to be
handled, it also generates a version in which different methods are
cached in a dictionary. If a given method has not yet been generated,
it creates a version specific to that dimensionality and then stores
it in the dictionary. Creating the method is slow---it involves
generating expressions and then evaluating them---but once created the
function can be looked up from the cache, and is reasonably efficient
(but still less efficient than the versions generated for explicit
dimensionality).
The arguments to ``@ngenerate`` are:
- The symbol of the variable that will be used for generating
different versions (in the example, ``N``)
- The return type of the function (in the example,
``typeof(A)``). This is not used for the versions that are generated
for specific ``N``, but is needed for the dictionary-backed
version. Julia cannot infer the return type of the function looked
up from the dictionary.
- The actual function declaration. Use ``N`` as you would a normal
parameter.
Anonymous-function expressions as macro arguments
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Perhaps the single most powerful feature in ``Cartesian`` is the
ability to supply anonymous-function expressions that get evaluated at
parsing time. Let's consider a simple example::
@nexprs 2 j->(i_j = 1)
``@nexprs`` generates ``n`` expressions that follow a pattern. This
code would generate the following statements::
i_1 = 1
i_2 = 1
In each generated statement, an "isolated" ``j`` (the variable of the
anonymous function) gets replaced by values in the range ``1:2``.
Generally speaking, Cartesian employs a LaTeX-like syntax. This
allows you to do math on the index ``j``. Here's an example computing
the strides of an array::
s_1 = 1
@nexprs 3 j->(s_{j+1} = s_j * size(A, j))
would generate expressions
::
s_1 = 1
s_2 = s_1 * size(A, 1)
s_3 = s_2 * size(A, 2)
s_4 = s_3 * size(A, 3)
Anonymous-function expressions have many uses in practice.
.. _devdoc-cartesian-reference:
Macro reference
~~~~~~~~~~~~~~~
Macros for creating functions
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. function:: @ngenerate Nsym returntypeexpr functiondeclexpr
Generate versions of a function for different values of ``Nsym``.
.. function:: @nsplat Nsym functiondeclexpr
@nsplat Nsym dimrange functiondeclexpr
Generate explicit versions of a function for different numbers of
arguments. For example::
@nsplat N 2:3 absgetindex(A, I::NTuple{N,Real}...) = abs(getindex(A, I...))
generates::
absgetindex(A, I_1::Real, I_2::Real) = abs(getindex(A, I_1, I_2))
absgetindex(A, I_1::Real, I_2::Real, I_3::Real) = abs(getindex(A, I_1, I_2, I_3))
Macros for function bodies
^^^^^^^^^^^^^^^^^^^^^^^^^^
.. function:: @nloops N itersym rangeexpr bodyexpr
@nloops N itersym rangeexpr preexpr bodyexpr
@nloops N itersym rangeexpr preexpr postexpr bodyexpr
Generate ``N`` nested loops, using ``itersym`` as the prefix for
the iteration variables. ``rangeexpr`` may be an
anonymous-function expression, or a simple symbol ``var`` in which
case the range is ``1:size(var,d)`` for dimension ``d``.
Optionally, you can provide "pre" and "post" expressions. These
get executed first and last, respectively, in the body of each
loop. For example,
::
@nloops 2 i A d->j_d=min(i_d,5) begin
s += @nref 2 A j
end
would generate
::
for i_2 = 1:size(A, 2)
j_2 = min(i_2, 5)
for i_1 = 1:size(A, 1)
j_1 = min(i_1, 5)
s += A[j_1,j_2]
end
end
If you want just a post-expression, supply
``nothing`` for the pre-expression. Using parenthesis and
semicolons, you can supply multi-statement expressions.
.. function:: @nref N A indexexpr
Generate expressions like ``A[i_1,i_2,...]``. ``indexexpr`` can
either be an iteration-symbol prefix, or an anonymous-function
expression.
.. function:: @nexprs N expr
Generate ``N`` expressions. ``expr`` should be an
anonymous-function expression.
.. function:: @ntuple N expr
Generates an ``N``-tuple. ``@ntuple 2 i`` would generate ``(i_1, i_2)``, and ``@ntuple 2 k->k+1`` would generate ``(2,3)``.
.. function:: @nall N expr
``@nall 3 d->(i_d > 1)`` would generate the expression
``(i_1 > 1 && i_2 > 1 && i_3 > 1)``. This can be convenient for
bounds-checking.
.. function:: @nif N conditionexpr expr
@nif N conditionexpr expr elseexpr
Generates a sequence of ``if ... elseif ... else ... end`` statements. For example::
@nif 3 d->(i_d >= size(A,d)) d->(error("Dimension ", d, " too big")) d->println("All OK")
would generate::
if i_1 > size(A, 1)
error("Dimension ", 1, " too big")
elseif i_2 > size(A, 2)
error("Dimension ", 2, " too big")
else
println("All OK")
end
Frequently asked questions
^^^^^^^^^^^^^^^^^^^^^^^^^^
I got an error ``ERROR: N not defined`` when using ``@ngenerate``. Why?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Most likely you forgot to define your function with ``N`` as a type parameter, e.g., ``@ngenerate N returntype myfunc{N}(...)``.
================================================
FILE: devdocs/index.rst
================================================
:orphan:
.. _devdocs-index:
####################################
Documentation of Julia's Internals
####################################
.. toctree::
:maxdepth: 1
cartesian
================================================
FILE: index.rst
================================================
%%%%%%%%%%%%
Julia 文档
%%%%%%%%%%%%
* :ref:`manual`
* :ref:`stdlib`
* :ref:`devdocs`
* :ref:`note`
.. _manual:
######
手册
######
.. toctree::
:maxdepth: 1
manual/introduction
manual/getting-started
manual/variables
manual/integers-and-floating-point-numbers
manual/mathematical-operations
manual/complex-and-rational-numbers
manual/strings
manual/functions
manual/control-flow
manual/variables-and-scoping
manual/types
manual/methods
manual/constructors
manual/conversion-and-promotion
manual/modules
manual/metaprogramming
manual/arrays
manual/linear-algebra
manual/networking-and-streams
manual/parallel-computing
manual/dates
manual/nullable-types
manual/interacting-with-julia
manual/running-external-programs
manual/calling-c-and-fortran-code
manual/embedding
manual/packages
manual/performance-tips
manual/style-guide
manual/faq
manual/noteworthy-differences
.. _stdlib:
########
标准库
########
.. toctree::
:maxdepth: 1
stdlib/base
stdlib/sparse
stdlib/linalg
stdlib/constants
stdlib/file
stdlib/punctuation
stdlib/sort
stdlib/pkg
stdlib/collections
stdlib/graphics
stdlib/dates
stdlib/test
stdlib/profile
.. _devdocs:
############
开发者文档
############
.. toctree::
:maxdepth: 1
devdocs/cartesian
.. _note:
######
笔记
######
.. toctree::
:maxdepth: 1
note/macroes
note/uses
note/optimize
================================================
FILE: latex.rst
================================================
:orphan:
%%%%%%%%%%%%%%%%%%%%%
Julia Documentation
%%%%%%%%%%%%%%%%%%%%%
.. toctree::
:maxdepth: 1
manual/index
note/index
stdlib/index
================================================
FILE: manual/arrays.rst
================================================
.. _man-arrays:
**********
多维数组
**********
.. **************************
.. Multi-dimensional Arrays
.. **************************
类似于其它科学计算语言,Julia语言提供了内置的数组。相较于很多科学计算语言都很关注数组在其它容器上的开销。Julia语言并不特别地对待数组。如同其它Julia代码一样,数组基本完全使用Julia本身实现,由编译器本身进行性能优化。同样的,这也使得通过继承 ``AbstractArray`` 来定制数组成为可能。 更多的细节,请参照 :ref: `抽象数组接口`。
.. Julia, like most technical computing languages, provides a first-class
.. array implementation. Most technical computing languages pay a lot of
.. attention to their array implementation at the expense of other
.. containers. Julia does not treat arrays in any special way. The array
.. library is implemented almost completely in Julia itself, and derives
.. its performance from the compiler, just like any other code written in
.. Julia. As such, it's also possible to define custom array types by
.. inheriting from ``AbstractArray.`` See the :ref:`manual section on the
.. AbstractArray interface <man-interfaces-abstractarray>` for more details
.. on implementing a custom array type.
数组是一个存在多维网格中的对象集合。通常,数组包含的对象的类型为 ``Any`` 。对大多数计算而言,数组对象一般更具体为 ``Float64`` 或 ``Int32`` 。
.. An array is a collection of objects stored in a multi-dimensional
.. grid. In the most general case, an array may contain objects of type
.. ``Any``. For most computational purposes, arrays should contain
.. objects of a more specific type, such as ``Float64`` or ``Int32``.
总的来说,不像其它的科学计算语言,Julia不需要为了获得高性能而将程序被写成向量化的形式。Julia的编译器使用类型推断生成优化的代码来进行数组索引,这样的编程风格在没有牺牲性能的同时,可读性更好,编写起来更方便,有时候还会使用更少的内存。
.. In general, unlike many other technical computing languages, Julia does
.. not expect programs to be written in a vectorized style for performance.
.. Julia's compiler uses type inference and generates optimized code for
.. scalar array indexing, allowing programs to be written in a style that
.. is convenient and readable, without sacrificing performance, and using
.. less memory at times.
有一些科学计算语言会通过值来传递数组,这在很多情况下很方便,而在 Julia 中,参数将通过引用传递给函数,这使得函数中对于一个数组输入的修改在函数外部是可见的。Julia 的库函数不会修改传递给它的输入。用户写代码时,如果要想做类似的功能,要注意先把输入复制一份儿。
.. In Julia, all arguments to functions are passed by reference. Some
.. technical computing languages pass arrays by value, and this is
.. convenient in many cases. In Julia, modifications made to input arrays
.. within a function will be visible in the parent function. The entire
.. Julia array library ensures that inputs are not modified by library
.. functions. User code, if it needs to exhibit similar behaviour, should
.. take care to create a copy of inputs that it may modify.
数组
====
.. Arrays
.. ======
基础函数
--------
.. Basic Functions
.. ---------------
=============== ========================================================================
函数 说明
=============== ========================================================================
``eltype(A)`` ``A`` 中元素的类型
``length(A)`` ``A`` 中元素的个数
``ndims(A)`` ``A`` 有几个维度
``size(A)`` 返回一个元素为 ``A`` 的维度的多元组
``size(A,n)`` ``A`` 在某个维度上的长度
``stride(A,k)`` 在维度 ``k`` 上,邻接元素(在内存中)的线性索引距离
``strides(A)`` 返回多元组,其元素为在每个维度上,邻接元素(在内存中)的线性索引距离
=============== ========================================================================
================================ ==============================================================================
函数 说明
================================ ==============================================================================
:func:`eltype(A) <eltype>` ``A`` 中元素的类型
:func:`length(A) <length>` ``A`` 中元素的个数
:func:`ndims(A) <ndims>` ``A`` 的维数
:func:`size(A) <size>` 返回一个包含 ``A`` 中每个维度元素个数的多元组
:func:`size(A,n) <size>` ``A`` 在某个维度上的大小
:func:`indices(A) <indices>` 返回一个包含 ``A`` 中可能的索引的多元组
:func:`indices(A,n) <indices>` 返回一个在 ``n`` 维上可能的索引范围
:func:`eachindex(A) <eachindex>` 一个能够高效地访问每个 ``A`` 中的元素的迭代器
:func:`stride(A,k) <stride>` 第``k``维的跨度(相临元素间的索引距离)
:func:`strides(A) <strides>` 返回一个包含每一维度跨度的多元组
================================ ==============================================================================
.. ================================ ==============================================================================
.. Function Description
.. ================================ ==============================================================================
.. :func:`eltype(A) <eltype>` the type of the elements contained in ``A``
.. :func:`length(A) <length>` the number of elements in ``A``
.. :func:`ndims(A) <ndims>` the number of dimensions of ``A``
.. :func:`size(A) <size>` a tuple containing the dimensions of ``A``
.. :func:`size(A,n) <size>` the size of ``A`` along a particular dimension
.. :func:`indices(A) <indices>` a tuple containing the valid indices of ``A``
.. :func:`indices(A,n) <indices>` a range expressing the valid indices along dimension ``n``
.. :func:`eachindex(A) <eachindex>` an efficient iterator for visiting each position in ``A``
.. :func:`stride(A,k) <stride>` the stride (linear index distance between adjacent elements) along dimension ``k``
.. :func:`strides(A) <strides>` a tuple of the strides in each dimension
.. ================================ ==============================================================================
构造和初始化
------------
.. Construction and Initialization
.. -------------------------------
下列函数中调用的 ``dims...`` 参数,既可以是维度的单多元组,也可以是维度作为可变参数时的一组值。
.. Many functions for constructing and initializing arrays are provided. In
.. the following list of such functions, calls with a ``dims...`` argument
.. can either take a single tuple of dimension sizes or a series of
.. dimension sizes passed as a variable number of arguments.
=================================================== =====================================================================
函数 描述
=================================================== =====================================================================
:func:`Array{type}(dims...) <Array>` 未初始化的稠密数组
:func:`zeros(type, dims...) <zeros>` 指定类型的全 0 数组. 如果未指明 ``type``, 默认为 ``Float64``
:func:`zeros(A) <zeros>` 全 0 数组, 元素类型和大小同 ``A``
:func:`ones(type, dims...) <ones>` 指定类型的全 1 数组. 如果未指明 ``type``, 默认为 ``Float64``
:func:`ones(A) <ones>` 全 1 数组, 元素类型和大小同 ``A``
:func:`trues(dims...) <trues>` 全 ``true`` 的 ``Bool`` 数组
:func:`trues(A) <trues>` 全 ``true`` 的 ``Bool`` 数组,大小和 ``A`` 相同
:func:`falses(dims...) <falses>` 全 ``false`` 的 ``Bool`` 数组
:func:`falses(A) <falses>` 全 ``false`` 的 ``Bool`` 数组,大小和 ``A`` 相同
:func:`reshape(A, dims...) <reshape>` 将数组 ``A`` 中的数据按照指定维度排列
:func:`copy(A) <copy>` 复制 ``A``
:func:`deepcopy(A) <deepcopy>` 深度拷贝,递归地复制 ``A`` 中的元素
:func:`similar(A, element_type, dims...) <similar>` 属性与输入数组(稠密、稀疏等)相同的未初始化数组,但指明了元素类型和维度。
第二、三参数可省略,省略时默认为 ``A`` 的元素类型和维度
:func:`reinterpret(type, A) <reinterpret>` 二进制数据与输入数组相同的数组,但指定了元素类型
:func:`rand(dims) <rand>` 在 [0,1) 上独立均匀同分布的 ``Float64`` 类型的随机数组
:func:`randn(dims) <randn>` ``Float64`` 类型的独立正态同分布的随机数组,均值为 0 ,标准差为 1
:func:`eye(n) <eye>` ``n`` x ``n`` 单位矩阵
:func:`eye(m, n) <eye>` ``m`` x ``n`` 单位矩阵
:func:`linspace(start, stop, n) <linspace>` 从 ``start`` 至 ``stop`` 的由 ``n`` 个元素构成的线性向量
:func:`fill!(A, x) <fill!>` 用值 ``x`` 填充数组 ``A``
:func:`fill(x, dims) <fill>` 创建指定规模的数组, 并使用 ``x`` 填充
=================================================== =====================================================================
.. .. [#] *iid*, independently and identically distributed.
一维数组(向量)可以通过使用``[A, B, C, ...]``这样的语句来构造。
.. The syntax ``[A, B, C, ...]`` constructs a 1-d array (vector) of its arguments.
连接
----
使用下列函数,可在任意维度连接数组:
================ ======================================================
Function Description
================ ======================================================
``cat(k, A...)`` 在第 ``k`` 维上连接给定的n维数组
``vcat(A...)`` ``cat(1, A...)``的简写
``hcat(A...)`` ``cat(2, A...)``的简写
================ ======================================================
传递给这些函数的参数值将被当做只有一个元素的数组
.. Scalar values passed to these functions are treated as 1-element arrays.
由于连接函数使用的次数很频繁,所以有一些专用的语法来调用它们
.. The concatenation functions are used so often that they have special syntax:
=================== =========
表达式 所调用的函数
=================== =========
``[A B C ...]`` ``hcat``
``[A, B, C, ...]`` ``vcat``
``[A B; C D; ...]`` ``hvcat``
=================== =========
``hvcat`` 同时连接第一维 (用分号隔开) 和第二维度
(用空格隔开).
.. :func:`hvcat` concatenates in both dimension 1 (with semicolons) and dimension 2
.. (with spaces).
指定类型的数组初始化
------------------------
指定类型为``T``的数组可以使用``T[A, B, C, ...]``来初始化. 这将会创建一个元素类型为``T``,元素初始化为``A``, ``B``, ``C``等的一维数组。比如``Any[x, y, z]``将创建一个包含任何类型的混合数组。
类似地,连接语句也能通过加前缀来指定元素类型
.. doctest::
julia> [[1 2] [3 4]]
1×4 Array{Int64,2}:
1 2 3 4
julia> Int8[[1 2] [3 4]]
1×4 Array{Int8,2}:
1 2 3 4
.. Typed array initializers
.. ------------------------
.. An array with a specific element type can be constructed using the syntax
.. ``T[A, B, C, ...]``. This will construct a 1-d array with element type
.. ``T``, initialized to contain elements ``A``, ``B``, ``C``, etc.
.. For example ``Any[x, y, z]`` constructs a heterogeneous array that can
.. contain any values.
.. Concatenation syntax can similarly be prefixed with a type to specify
.. the element type of the result.
.. .. doctest::
.. julia> [[1 2] [3 4]]
.. 1×4 Array{Int64,2}:
.. 1 2 3 4
.. julia> Int8[[1 2] [3 4]]
.. 1×4 Array{Int8,2}:
.. 1 2 3 4
.. _comprehensions:
列表推导
--------------
列表推导为构造数组提供了一种更加一般,更加强大的方法。它的语法类似于数学中的集合标记法: ::
A = [ F(x,y,...) for x=rx, y=ry, ... ]
``F(x,y,...)`` 根据变量 ``x``, ``y`` 等来求值。这些变量的值可以是任何迭代对象,但大多数情况下,都使用类似于 ``1:n`` 或 ``2:(n-1)`` 的范围对象,或显式指明为类似 ``[1.2, 3.4, 5.7]`` 的数组。它的结果是一个 N 维稠密数组。
.. Comprehensions provide a general and powerful way to construct arrays.
.. Comprehension syntax is similar to set construction notation in
.. mathematics
.. A = [ F(x,y,...) for x=rx, y=ry, ... ]
.. The meaning of this form is that ``F(x,y,...)`` is evaluated with the
.. variables ``x``, ``y``, etc. taking on each value in their given list of
.. values. Values can be specified as any iterable object, but will
.. commonly be ranges like ``1:n`` or ``2:(n-1)``, or explicit arrays of
.. values like ``[1.2, 3.4, 5.7]``. The result is an N-d dense array with
.. dimensions that are the concatenation of the dimensions of the variable
.. ranges ``rx``, ``ry``, etc. and each ``F(x,y,...)`` evaluation returns a
.. scalar.
下例计算在维度 1 上,当前元素及左右邻居元素的加权平均数:
.. The following example computes a weighted average of the current element
.. and its left and right neighbor along a 1-d grid. :
.. testsetup:: *
srand(314)
.. doctest:: array-rand
julia> x = rand(8)
8-element Array{Float64,1}:
0.843025
0.869052
0.365105
0.699456
0.977653
0.994953
0.41084
0.809411
julia> [ 0.25*x[i-1] + 0.5*x[i] + 0.25*x[i+1] for i=2:length(x)-1 ]
6-element Array{Float64,1}:
0.736559
0.57468
0.685417
0.912429
0.8446
0.656511
输出的数组类型由所计算出的元素类型决定。显式地控制类型可以通过在列表推导的前面加上类型前缀完成。例如,我们可以这样来使得结果都是单精度的浮点数
Float32[ 0.25*x[i-1] + 0.5*x[i] + 0.25*x[i+1] for i=2:length(x)-1 ]
.. The resulting array type depends on the types of the computed elements.
.. In order to control the type explicitly, a type can be prepended to the comprehension.
.. For example, we could have requested the result in single precision by writing::
.. Float32[ 0.25*x[i-1] + 0.5*x[i] + 0.25*x[i+1] for i=2:length(x)-1 ]
.. _man-generator-expressions:
生成器表达式
---------------------
列表推导也可以被用不闭合的方括号写出,从而产生一个称为生成器的对象。这个对象可以通过迭代来产生所需的值,而不需要提前为一个数组分配内存。
(参见 :ref:`man-interfaces-iteration`)。
例如下面的表达式会对一列没有分配内存的数求和
.. doctest::
julia> sum(1/n^2 for n=1:1000)
1.6439345666815615
在生成器参数列表中有多个维度的时候,需要通过括号来分割各个参数::
julia> map(tuple, 1/(i+j) for i=1:2, j=1:2, [1:4;])
ERROR: syntax: invalid iteration specification
所有在 ``for`` 之后通过逗号分割的表达式将被解释成范围。通过增加括号能够使得我们给 ``map`` 增加第三个参数:
.. doctest::
julia> map(tuple, (1/(i+j) for i=1:2, j=1:2), [1 3; 2 4])
2×2 Array{Tuple{Float64,Int64},2}:
(0.5,1) (0.333333,3)
(0.333333,2) (0.25,4)
生成器和列表推导的范围可以通过多个``for``关键字对外层范围依赖:
.. doctest::
julia> [(i,j) for i=1:3 for j=1:i]
6-element Array{Tuple{Int64,Int64},1}:
(1,1)
(2,1)
(2,2)
(3,1)
(3,2)
(3,3)
在上面发的情况中,结果都会是一维数组
生成的值可以通过 ``if`` 关键字过滤
.. doctest::
julia> [(i,j) for i=1:3 for j=1:i if i+j == 4]
2-element Array{Tuple{Int64,Int64},1}:
(2,2)
(3,1)
.. .. _man-generator-expressions:
.. Generator Expressions
.. ---------------------
.. Comprehensions can also be written without the enclosing square brackets, producing
.. an object known as a generator. This object can be iterated to produce values on
.. demand, instead of allocating an array and storing them in advance
.. (see :ref:`man-interfaces-iteration`).
.. For example, the following expression sums a series without allocating memory:
.. .. doctest::
.. julia> sum(1/n^2 for n=1:1000)
.. 1.6439345666815615
.. When writing a generator expression with multiple dimensions inside an argument
.. list, parentheses are needed to separate the generator from subsequent arguments::
.. julia> map(tuple, 1/(i+j) for i=1:2, j=1:2, [1:4;])
.. ERROR: syntax: invalid iteration specification
.. All comma-separated expressions after ``for`` are interpreted as ranges. Adding
.. parentheses lets us add a third argument to ``map``:
.. .. doctest::
.. julia> map(tuple, (1/(i+j) for i=1:2, j=1:2), [1 3; 2 4])
.. 2×2 Array{Tuple{Float64,Int64},2}:
.. (0.5,1) (0.333333,3)
.. (0.333333,2) (0.25,4)
.. Ranges in generators and comprehensions can depend on previous ranges by writing
.. multiple ``for`` keywords:
.. .. doctest::
.. julia> [(i,j) for i=1:3 for j=1:i]
.. 6-element Array{Tuple{Int64,Int64},1}:
.. (1,1)
.. (2,1)
.. (2,2)
.. (3,1)
.. (3,2)
.. (3,3)
.. In such cases, the result is always 1-d.
.. Generated values can be filtered using the ``if`` keyword:
.. .. doctest::
.. julia> [(i,j) for i=1:3 for j=1:i if i+j == 4]
.. 2-element Array{Tuple{Int64,Int64},1}:
.. (2,2)
.. (3,1)
.. _man-array-indexing:
索引
----
索引 n 维数组 A 的通用语法为: ::
X = A[I_1, I_2, ..., I_n]
其中 I\_k 可以是:
1. 标量
2. 满足 ``:``, ``a:b``, 或 ``a:b:c`` 格式的 ``Range`` 对象
3. 能够选取整个维度的``:``或者``Colon()``
4. 任意整数数组,包括空数组 ``[]``
5. 能够输出所在位置为``true``的索引所对应元素的布尔数组
.. The general syntax for indexing into an n-dimensional array A is
.. X = A[I_1, I_2, ..., I_n]
.. where each I\_k may be:
.. 1. A scalar integer
.. 2. A ``Range`` of the form ``a:b``, or ``a:b:c``
.. 3. A ``:`` or ``Colon()`` to select entire dimensions
.. 4. An arbitrary integer array, including the empty array ``[]``
.. 5. A boolean array to select a vector of elements at its ``true`` indices
如果所有的索引都是标量,那么结果 ``X`` 就是 ``A`` 中的单个元素。不然 ``X``就是一个和索引有相同维度的数组。
.. If all the indices are scalars, then the result ``X`` is a single element from
.. the array ``A``. Otherwise, ``X`` is an array with the same number of
.. dimensions as the sum of the dimensionalities of all the indices.
例如如果所有的索引都是向量,那么 ``X``的大小就会是``(length(I_1), length(I_2), ..., length(I_n))``,``X``位于``(i_1, i_2, ..., i_n)``的元素具有``A[I_1[i_1], I_2[i_2], ..., I_n[i_n]]``的值。如果``I_1``被变为一个两维的矩阵,这个矩阵就会给``X``增加一个维度,那么``X``就会是一个``n+1``维的数组,大小为``(size(I_1, 1), size(I_1, 2), length(I_2), ..., length(I_n))``。位于``(i_1, i_2, i_3, ..., i_{n+1})``的元素就会有``A[I_1[i_1, i_2], I_2[i_3], ..., I_n[i_{n+1}]]``的值。所有用标量索引的维度的大小会被忽略。比如,``A[2, I, 3]``的结果是一个具有 ``size(I)`` 大小的数组。它的第 ``i``\ th 个元素是``A[2, I[i], 3]``。
.. If all indices are vectors, for example, then the shape of ``X`` would be
.. ``(length(I_1), length(I_2), ..., length(I_n))``, with location
.. ``(i_1, i_2, ..., i_n)`` of ``X`` containing the value
.. ``A[I_1[i_1], I_2[i_2], ..., I_n[i_n]]``. If ``I_1`` is changed to a
.. two-dimensional matrix, then ``X`` becomes an ``n+1``-dimensional array of
.. shape ``(size(I_1, 1), size(I_1, 2), length(I_2), ..., length(I_n))``. The
.. matrix adds a dimension. The location ``(i_1, i_2, i_3, ..., i_{n+1})`` contains
.. the value at ``A[I_1[i_1, i_2], I_2[i_3], ..., I_n[i_{n+1}]]``. All dimensions
.. indexed with scalars are dropped. For example, the result of ``A[2, I, 3]`` is
.. an array with size ``size(I)``. Its ``i``\ th element is populated by
.. ``A[2, I[i], 3]``.
使用布尔数组``B``通过:func:`find(B) <find>`进行索引和通过向量索引实际上是类似的。它们通常被称作逻辑索引,这将选出那些``B``中值为``true``的元素所在的索引在``A``中的值。一个逻辑索引必须是一个和对应维度有着同样长度的向量,或者是唯一一个和被索引数组的维度以及大小相同的索引。直接使用布尔数组进行索引一般比用:func:`find(B) <find>`进行索引更快。
.. Indexing by a boolean array ``B`` is effectively the same as indexing by the
.. vector that is returned by :func:`find(B) <find>`. Often referred to as logical
.. indexing, this selects elements at the indices where the values are ``true``,
.. akin to a mask. A logical index must be a vector of the same length as the
.. dimension it indexes into, or it must be the only index provided and match the
.. size and dimensionality of the array it indexes into. It is generally more
.. efficient to use boolean arrays as indices directly instead of first calling
.. :func:`find`.
进一步,多维数组的单个元素可以用``x = A[I]``索引,这里``I`` 是一个 ``CartesianIndex``(笛卡尔坐标)。它实际上类似于一个 整数``n``元组。具体参见下面的:ref:`man-array-iteration`
.. Additionally, single elements of a multidimensional array can be indexed as
.. ``x = A[I]``, where ``I`` is a ``CartesianIndex``. It effectively behaves like
.. an ``n``-tuple of integers spanning multiple dimensions of ``A``. See
.. :ref:`man-array-iteration` below.
``end``关键字是这里比较特殊的一个语法,由于最内层被索引的数组的大小会被确定,它可以在索引的括号中用来表示每个维度最后一个索引。不使用``end``关键字的索引与使用``getindex``一样::
X = getindex(A, I_1, I_2, ..., I_n)
.. As a special part of this syntax, the ``end`` keyword may be used to represent
.. the last index of each dimension within the indexing brackets, as determined by
.. the size of the innermost array being indexed. Indexing syntax without the
.. ``end`` keyword is equivalent to a call to ``getindex``::
.. X = getindex(A, I_1, I_2, ..., I_n)
例子:
.. doctest::
julia> x = reshape(1:16, 4, 4)
4×4 Base.ReshapedArray{Int64,2,UnitRange{Int64},Tuple{}}:
1 5 9 13
2 6 10 14
3 7 11 15
4 8 12 16
julia> x[2:3, 2:end-1]
2×2 Array{Int64,2}:
6 10
7 11
julia> x[map(ispow2, x)]
5-element Array{Int64,1}:
1
2
4
8
16
julia> x[1, [2 3; 4 1]]
2×2 Array{Int64,2}:
5 9
13 1
类似于``n:n-1``的空范围有时可以用来表示索引之间的位置。例如``searchsorted``函数使用这个方法来表示在有序数组中没有出现的元素:
.. doctest::
julia> a = [1,2,5,6,7];
julia> searchsorted(a, 3)
3:2
.. Empty ranges of the form ``n:n-1`` are sometimes used to indicate the inter-index
.. location between ``n-1`` and ``n``. For example, the ``searchsorted`` function uses
.. this convention to indicate the insertion point of a value not found in a sorted
.. array:
.. .. doctest::
.. julia> a = [1,2,5,6,7];
.. julia> searchsorted(a, 3)
.. 3:2
赋值
----
给 n 维数组 A 赋值的通用语法为: ::
A[I_1, I_2, ..., I_n] = X
其中 I\_k 可能是:
1. 标量
2. 满足 ``:``, ``a:b``, 或 ``a:b:c`` 格式的 ``Range`` 对象
3. 能够选取整个维度的``:``或者``Colon()``
4. 任意整数数组,包括空数组 ``[]``
5. 能够输出所在位置为``true``的索引所对应元素的布尔数组
.. Assignment
.. ----------
.. The general syntax for assigning values in an n-dimensional array A is::
.. A[I_1, I_2, ..., I_n] = X
.. where each ``I_k`` may be:
.. 1. A scalar integer
.. 2. A ``Range`` of the form ``a:b``, or ``a:b:c``
.. 3. A ``:`` or ``Colon()`` to select entire dimensions
.. 4. An arbitrary integer array, including the empty array ``[]``
.. 5. A boolean array to select elements at its ``true`` indices
如果 ``X`` 是一个数组,它的维度应为 ``(length(I_1), length(I_2), ..., length(I_n))`` ,且 ``A`` 在 ``i_1, i_2, ..., i_n`` 处的值被覆写为 ``X[I_1[i_1], I_2[i_2], ..., I_n[i_n]]`` 。如果 ``X`` 不是数组,它的值被写进所有 ``A`` 被引用的地方。
.. If ``X`` is an array, it must have the same number of elements as the product
.. of the lengths of the indices:
.. ``prod(length(I_1), length(I_2), ..., length(I_n))``. The value in location
.. ``I_1[i_1], I_2[i_2], ..., I_n[i_n]`` of ``A`` is overwritten with the value
.. ``X[i_1, i_2, ..., i_n]``. If ``X`` is not an array, its value
.. is written to all referenced locations of ``A``.
用于索引的布尔值向量与 ``getindex`` 中一样(先由 ``find`` 函数进行转换)。
.. A boolean array used as an index behaves as in :func:`getindex`, behaving as
.. though it is first transformed with :func:`find`.
索引赋值语法等价于调用 ``setindex!`` : ::
setindex!(A, X, I_1, I_2, ..., I_n)
例如:
.. doctest::
julia> x = reshape(1:9, 3, 3)
3x3 Array{Int64,2}:
1 4 7
2 5 8
3 6 9
julia> x[1:2, 2:3] = -1
-1
julia> x
3x3 Array{Int64,2}:
1 -1 -1
2 -1 -1
3 6 9
.. Index assignment syntax is equivalent to a call to ``setindex!``
.. setindex!(A, X, I_1, I_2, ..., I_n)
.. Example:
.. _man-array-iteration:
迭代
---------
我们建议使用下面的方法迭代整个数组::
for a in A
# Do something with the element a
end
for i in eachindex(A)
# Do something with i and/or A[i]
end
.. The recommended ways to iterate over a whole array are
.. ::
.. for a in A
.. # Do something with the element a
.. end
.. for i in eachindex(A)
.. # Do something with i and/or A[i]
.. end
在你需要使用具体的值而不是每个元素的索引的时候,使用第一个方法。在第二种方法里,如果``A``是一个有快速线性索引的数组, ``i``将是一个``Int`` 类型,否则将会是``CartesianIndex``::
A = rand(4,3)
B = view(A, 1:3, 2:3)
julia> for i in eachindex(B)
@show i
end
i = Base.IteratorsMD.CartesianIndex_2(1,1)
i = Base.IteratorsMD.CartesianIndex_2(2,1)
i = Base.IteratorsMD.CartesianIndex_2(3,1)
i = Base.IteratorsMD.CartesianIndex_2(1,2)
i = Base.IteratorsMD.CartesianIndex_2(2,2)
i = Base.IteratorsMD.CartesianIndex_2(3,2)
.. The first construct is used when you need the value, but not index, of each element. In the second construct, ``i`` will be an ``Int`` if ``A`` is an array
.. type with fast linear indexing; otherwise, it will be a ``CartesianIndex``::
.. A = rand(4,3)
.. B = view(A, 1:3, 2:3)
.. julia> for i in eachindex(B)
.. @show i
.. end
.. i = Base.IteratorsMD.CartesianIndex_2(1,1)
.. i = Base.IteratorsMD.CartesianIndex_2(2,1)
.. i = Base.IteratorsMD.CartesianIndex_2(3,1)
.. i = Base.IteratorsMD.CartesianIndex_2(1,2)
.. i = Base.IteratorsMD.CartesianIndex_2(2,2)
.. i = Base.IteratorsMD.CartesianIndex_2(3,2)
相较``for i = 1:length(A)``,使用``eachindex``更加高效。
.. In contrast with ``for i = 1:length(A)``, iterating with ``eachindex`` provides an efficient way to iterate over any array type.
数组的特性
------------
.. Array traits
.. ------------
如果你写了一个定制的 ``AbstractArray`` 类型,你可以用下面的方法声明它有快速线性索引::
Base.linearindexing{T<:MyArray}(::Type{T}) = LinearFast()
.. If you write a custom :obj:`AbstractArray` type, you can specify that it has fast linear indexing using
.. ::
.. Base.linearindexing{T<:MyArray}(::Type{T}) = LinearFast()
这个设置会让 ``MyArray``(你所定义的数组类型)的 ``eachindex`` 的迭代使用整数类型。如果你没有声明这个特性,那么会默认使用 ``LinearSlow()``。
.. This setting will cause ``eachindex`` iteration over a ``MyArray`` to use integers. If you don't specify this trait, the default value ``LinearSlow()`` is used.
向量化的运算符和函数
--------------------
数组支持下列运算符。逐元素进行的运算,应使用带“点”(逐元素)版本的二元运算符。
1. 一元: ``-``, ``+``, ``!``
2. 二元: ``+``, ``-``, ``*``, ``.*``, ``/``, ``./``,
``\``, ``.\``, ``^``, ``.^``, ``div``, ``mod``
3. 比较: ``.==``, ``.!=``, ``.<``, ``.<=``, ``.>``, ``.>=``
4. 一元布尔值或位运算: ``~``
5. 二元布尔值或位运算: ``&``, ``|``, ``$``
有一些运算符在没有``.``运算符的时候,由于有一个参数是标量同样是是逐元素运算的。这些运算符是``*``, ``+``, ``-``,和位运算符。``/`` 和 ``\`` 运算符在分母是标量时也是逐元素计算的。
.. Some operators without dots operate elementwise anyway when one argument is a
.. scalar. These operators are ``*``, ``+``, ``-``, and the bitwise operators. The
.. operators ``/`` and ``\`` operate elementwise when the denominator is a scalar.
注意比较运算,在给定一个布尔值的时候,是对整个数组进行的,比如``==``。在逐元素比较时请使用``.``运算符。
.. Note that comparisons such as ``==`` operate on whole arrays, giving a single
.. boolean answer. Use dot operators for elementwise comparisons.
Julia为将操作广播至整个数组或者数组和标量的混合变量中,提供了 ``f.(args...)`` 这样的兼容语句。这样会使调用向量化的数学操作或者其它运算更加方便。例如 ``sin.(x)`` 或者 ``min.(x,y)``。(广播操作)详见 :ref:`man-dot-vectorizing`
.. To enable convenient vectorization of mathematical and other operations, Julia provides
.. the compact syntax ``f.(args...)``, e.g. ``sin.(x)`` or ``min.(x,y)``, for elementwise
.. operations over arrays or mixtures of arrays and scalars (a :func:`broadcast` operation).
.. See :ref:`man-dot-vectorizing`.
注意 ``min`` ``max`` 和 ``minimum`` ``maximum`` 之间的区别,前者是对多个数组操作,找出各数组对应的的元素中的最大最小,后者是作用在一个数组上找出该数组的最大最小值。
.. Note that there is a difference between ``min`` and ``max``, which operate
.. elementwise over multiple array arguments, and ``minimum`` and ``maximum``, which
.. find the smallest and largest values within an array.
.. _man-broadcasting:
广播
------------
有时要对不同维度的数组进行逐元素的二元运算,如将向量加到矩阵的每一列。低效的方法是,把向量复制成同维度的矩阵:
.. It is sometimes useful to perform element-by-element binary operations
.. on arrays of different sizes, such as adding a vector to each column
.. of a matrix. An inefficient way to do this would be to replicate the
.. vector to the size of the matrix:
.. doctest::
julia> a = rand(2,1); A = rand(2,3);
julia> repmat(a,1,3)+A
2x3 Array{Float64,2}:
1.20813 1.82068 1.25387
1.56851 1.86401 1.67846
维度很大时,效率会很低。Julia 提供 ``broadcast`` 函数,它将数组参数的维度进行扩展,使其匹配另一个数组的对应维度,且不需要额外内存,最后再逐元素调用指定的二元函数:
.. This is wasteful when dimensions get large, so Julia offers
.. :func:`broadcast`, which expands singleton dimensions in
.. array arguments to match the corresponding dimension in the other
.. array without using extra memory, and applies the given
.. function elementwise:
.. doctest::
julia> broadcast(+, a, A)
2x3 Array{Float64,2}:
1.20813 1.82068 1.25387
1.56851 1.86401 1.67846
julia> b = rand(1,2)
1x2 Array{Float64,2}:
0.867535 0.00457906
julia> broadcast(+, a, b)
2x2 Array{Float64,2}:
1.71056 0.847604
1.73659 0.873631
逐元素的运算符,如 ``.+`` 和 ``.*`` 将会在必要时进行 broadcasting 。还提供了 ``broadcast!`` 函数,可以明确指明目的,而 ``broadcast_getindex`` 和 ``broadcast_setindex!`` 函数可以在索引前对索引值做 broadcast 。
.. Elementwise operators such as ``.+`` and ``.*`` perform broadcasting if necessary. There is also a :func:`broadcast!` function to specify an explicit destination, and :func:`broadcast_getindex` and :func:`broadcast_setindex!` that broadcast the indices before indexing. Moreover, ``f.(args...)`` is equivalent to ``broadcast(f, args...)``, providing a convenient syntax to broadcast any function (:ref:`man-dot-vectorizing`).
并且,``broadcast`` 不仅限于数组(参见函数的文档),它也能用于多元组和并将不是数组和多元组的参数当做“标量”对待。
.. Additionally, :func:`broadcast` is not limited to arrays (see the function documentation), it also handles tuples and treats any argument that is not an array or a tuple as a "scalar".
.. doctest::
julia> convert.(Float32, [1, 2])
2-element Array{Float32,1}:
1.0
2.0
julia> ceil.((UInt8,), [1.2 3.4; 5.6 6.7])
2×2 Array{UInt8,2}:
0x02 0x04
0x06 0x07
julia> string.(1:3, ". ", ["First", "Second", "Third"])
3-element Array{String,1}:
"1. First"
"2. Second"
"3. Third"
实现
----
Julia 的基础数组类型是抽象类型 ``AbstractArray{T,N}`` ,其中维度为 ``N`` ,元素类型为 ``T`` 。 ``AbstractVector`` 和 ``AbstractMatrix`` 分别是它 1 维 和 2 维的别名。
``AbstractArray`` 类型包含任何形似数组的类型, 而且它的实现和通常的数组会很不一样。例如,任何具体的 ``AbstractArray{T,N}`` 至少要有 ``size(A)`` (返回 ``Int`` 多元组), ``getindex(A,i)`` 和 ``getindex(A,i1,...,iN)`` (返回 ``T`` 类型的一个元素), 可变的数组要能 ``setindex!``。 这些操作都要求在近乎常数的时间复杂度或 O(1) 复杂度,否则某些数组函数就会特别慢。具体的类型也要提供类似于 ``similar(A,T=eltype(A),dims=size(A))`` 的方法用来分配一个拷贝。
.. The :obj:`AbstractArray` type includes anything vaguely array-like, and
.. implementations of it might be quite different from conventional
.. arrays. For example, elements might be computed on request rather than
.. stored. However, any concrete ``AbstractArray{T,N}`` type should
.. generally implement at least :func:`size(A) <size>` (returning an ``Int`` tuple),
.. :func:`getindex(A,i) <getindex>` and :func:`getindex(A,i1,...,iN) <getindex>`;
.. mutable arrays should also implement :func:`setindex!`. It
.. is recommended that these operations have nearly constant time complexity,
.. or technically Õ(1) complexity, as otherwise some array functions may
.. be unexpectedly slow. Concrete types should also typically provide
.. a :func:`similar(A,T=eltype(A),dims=size(A)) <similar>` method, which is used to allocate
.. a similar array for :func:`copy` and other out-of-place operations.
.. No matter how an ``AbstractArray{T,N}`` is represented internally,
.. ``T`` is the type of object returned by *integer* indexing (``A[1,
.. ..., 1]``, when ``A`` is not empty) and ``N`` should be the length of
.. the tuple returned by :func:`size`.
``DenseArray`` 是``AbstractArray``的一个抽象子类型,它包含了所有的在内存中使用常规形式分配内存,并且也因此能够传递给C和Fortran语言的数组。子类型需要提供``stride(A,k)``方法用以返回第``k``维的间隔:给维度 ``k`` 索引增加 ``1`` 将会给 :func:`getindex(A,i) <getindex>` 的第 ``i`` 个索引增加 :func:`stride(A,k) <stride>`。 如果提供了指针的转换函数:func:`Base.unsafe_convert(Ptr{T}, A) <unsafe_convert>` 那么,内存的分布将会和这些维度的间隔相同。
.. :obj:`DenseArray` is an abstract subtype of :obj:`AbstractArray` intended
.. to include all arrays that are laid out at regular offsets in memory,
.. and which can therefore be passed to external C and Fortran functions
.. expecting this memory layout. Subtypes should provide a method
.. :func:`stride(A,k) <stride>` that returns the "stride" of dimension ``k``:
.. increasing the index of dimension ``k`` by ``1`` should increase the
.. index ``i`` of :func:`getindex(A,i) <getindex>` by :func:`stride(A,k) <stride>`. If a
.. pointer conversion method :func:`Base.unsafe_convert(Ptr{T}, A) <unsafe_convert>` is provided, the
.. memory layout should correspond in the same way to these strides.
``Array{T,N}`` 类型是 ``DenseArray`` 的特殊实例,它的元素以列序为主序存储(详见 :ref:`man-performance-tips` )。 ``Vector`` 和 ``Matrix`` 是分别是它 1 维 和 2 维的别名。
.. The :obj:`Array` type is a specific instance of :obj:`DenseArray`
.. where elements are stored in column-major order (see additional notes in
.. :ref:`man-performance-tips`). :obj:`Vector` and :obj:`Matrix` are aliases for
.. the 1-d and 2-d cases. Specific operations such as scalar indexing,
.. assignment, and a few other basic storage-specific operations are all
.. that have to be implemented for :obj:`Array`, so that the rest of the array
.. library can be implemented in a generic manner.
``SubArray`` 是 ``AbstractArray`` 的特殊实例,它通过引用而不是复制来进行索引。使用 ``sub`` 函数来构造 ``SubArray`` ,它的调用方式与 ``getindex`` 相同(使用数组和一组索引参数)。 ``sub`` 的结果与 ``getindex`` 的结果类似,但它的数据仍留在原地。 ``sub`` 在 ``SubArray`` 对象中保存输入的索引向量,这个向量将被用来间接索引原数组。
.. :obj:`SubArray` is a specialization of :obj:`AbstractArray` that performs
.. indexing by reference rather than by copying. A :obj:`SubArray` is created
.. with the :func:`view` function, which is called the same way as :func:`getindex`
.. (with an array and a series of index arguments). The result of :func:`view` looks
.. the same as the result of :func:`getindex`, except the data is left in place.
.. :func:`view` stores the input index vectors in a :obj:`SubArray` object, which
.. can later be used to index the original array indirectly.
``StridedVector`` 和 ``StridedMatrix`` 是为了方便而定义的别名。通过给他们传递 ``Array`` 或 ``SubArray`` 对象,可以使 Julia 大范围调用 BLAS 和 LAPACK 函数,提高内存申请和复制的效率。
.. :obj:`StridedVector` and :obj:`StridedMatrix` are convenient aliases defined
.. to make it possible for Julia to call a wider range of BLAS and LAPACK
.. functions by passing them either :obj:`Array` or :obj:`SubArray` objects, and
.. thus saving inefficiencies from memory allocation and copying.
下面的例子计算大数组中的一个小块的 QR 分解,无需构造临时变量,直接调用合适的 LAPACK 函数。
.. The following example computes the QR decomposition of a small section
.. of a larger array, without creating any temporaries, and by calling the
.. appropriate LAPACK function with the right leading dimension size and
.. stride parameters.
.. doctest::
julia> a = rand(10,10)
10×10 Array{Float64,2}:
0.561255 0.226678 0.203391 0.308912 … 0.750307 0.235023 0.217964
0.718915 0.537192 0.556946 0.996234 0.666232 0.509423 0.660788
0.493501 0.0565622 0.118392 0.493498 0.262048 0.940693 0.252965
0.0470779 0.736979 0.264822 0.228787 0.161441 0.897023 0.567641
0.343935 0.32327 0.795673 0.452242 0.468819 0.628507 0.511528
0.935597 0.991511 0.571297 0.74485 … 0.84589 0.178834 0.284413
0.160706 0.672252 0.133158 0.65554 0.371826 0.770628 0.0531208
0.306617 0.836126 0.301198 0.0224702 0.39344 0.0370205 0.536062
0.890947 0.168877 0.32002 0.486136 0.096078 0.172048 0.77672
0.507762 0.573567 0.220124 0.165816 0.211049 0.433277 0.539476
julia> b = view(a, 2:2:8,2:2:4)
4×2 SubArray{Float64,2,Array{Float64,2},Tuple{StepRange{Int64,Int64},StepRange{Int64,Int64}},false}:
0.537192 0.996234
0.736979 0.228787
0.991511 0.74485
0.836126 0.0224702
julia> (q,r) = qr(b);
julia> q
4×2 Array{Float64,2}:
-0.338809 0.78934
-0.464815 -0.230274
-0.625349 0.194538
-0.527347 -0.534856
julia> r
2×2 Array{Float64,2}:
-1.58553 -0.921517
0.0 0.866567
稀疏矩阵
========
`稀疏矩阵 <http://zh.wikipedia.org/zh-cn/%E7%A8%80%E7%96%8F%E7%9F%A9%E9%98%B5>`_ 是其元素大部分为 0 ,并以特殊的形式来节省空间和执行时间的存储数据的矩阵。稀疏矩阵适用于当使用这些稀疏矩阵的表示方式能够获得明显优于稠密矩阵的情况。
列压缩(CSC)存储
-----------------
Julia 中,稀疏矩阵使用 `列压缩(CSC)格式 <http://en.wikipedia.org/wiki/Sparse_matrix#Compressed_sparse_column_.28CSC_or_CCS.29>`_ 。Julia 稀疏矩阵的类型为 ``SparseMatrixCSC{Tv,Ti}`` ,其中 ``Tv`` 是非零元素的类型, ``Ti`` 是整数类型,存储列指针和行索引: ::
type SparseMatrixCSC{Tv,Ti<:Integer} <: AbstractSparseMatrix{Tv,Ti}
m::Int # Number of rows
n::Int # Number of columns
colptr::Vector{Ti} # Column i is in colptr[i]:(colptr[i+1]-1)
rowval::Vector{Ti} # Row values of nonzeros
nzval::Vector{Tv} # Nonzero values
end
列压缩存储便于按列简单快速地存取稀疏矩阵的元素,但按行存取则较慢。把非零值插入 CSC 结构等运算,都比较慢,这是因为稀疏矩阵中,在所插入元素后面的元素,都要逐一移位。
如果你从其他地方获得的数据是 CSC 格式储存的,想用 Julia 来读取,应确保它的序号从 1 开始索引。每一列中的行索引值应该是排好序的。如果你的 `SparseMatrixCSC` 对象包含未排序的行索引值,对它们进行排序的最快的方法是转置两次。
.. If you have data in CSC format from a different application or library,
.. and wish to import it in Julia, make sure that you use 1-based indexing.
.. The row indices in every column need to be sorted. If your `SparseMatrixCSC`
.. object contains unsorted row indices, one quick way to sort them is by
.. doing a double transpose.
有时,在 `SparseMatrixCSC` 中存储一些零值,后面的运算比较方便。 ``Base`` 中允许这种行为(但是不保证在操作中会一直保留这些零值)。这些被存储的零被许多函数认为是非零值。 ``nnz`` 函数返回稀疏数据结构中存储的元素数目,包括被存储的零。要想得到准确的非零元素的数目,请使用 ``countnz`` 函数,它挨个检查每个元素的值(因此它的时间复杂度不再是常数,而是与元素数目成正比)。
.. In some applications, it is convenient to store explicit zero values
.. in a `SparseMatrixCSC`. These *are* accepted by functions in ``Base``
.. (but there is no guarantee that they will be preserved in mutating
.. operations). Such explicitly stored zeros are treated as structural
.. nonzeros by many routines. The ``nnz`` function returns the number of
.. elements explicitly stored in the sparse data structure,
.. including structural nonzeros. In order to count the exact number of actual
.. values that are nonzero, use ``countnz``, which inspects every stored
.. element of a sparse matrix.
构造稀疏矩阵
------------
稠密矩阵有 ``zeros`` 和 ``eye`` 函数,稀疏矩阵对应的函数,在函数名前加 ``sp`` 前缀即可:
.. doctest::
julia> spzeros(3,5)
3x5 sparse matrix with 0 Float64 entries:
julia> speye(3,5)
3x5 sparse matrix with 3 Float64 entries:
[1, 1] = 1.0
[2, 2] = 1.0
[3, 3] = 1.0
``sparse`` 函数是比较常用的构造稀疏矩阵的方法。它输入行索引 ``I`` ,列索引向量 ``J`` ,以及非零值向量 ``V`` 。 ``sparse(I,J,V)`` 构造一个满足 ``S[I[k], J[k]] = V[k]`` 的稀疏矩阵:
.. doctest::
julia> I = [1, 4, 3, 5]; J = [4, 7, 18, 9]; V = [1, 2, -5, 3];
julia> S = sparse(I,J,V)
5x18 sparse matrix with 4 Int64 entries:
[1 , 4] = 1
[4 , 7] = 2
[5 , 9] = 3
[3 , 18] = -5
与 ``sparse`` 相反的函数为 ``findn`` ,它返回构造稀疏矩阵时的输入:
.. doctest::
julia> findn(S)
([1,4,5,3],[4,7,9,18])
julia> findnz(S)
([1,4,5,3],[4,7,9,18],[1,2,3,-5])
另一个构造稀疏矩阵的方法是,使用 ``sparse`` 函数将稠密矩阵转换为稀疏矩阵:
.. doctest::
julia> sparse(eye(5))
5x5 sparse matrix with 5 Float64 entries:
[1, 1] = 1.0
[2, 2] = 1.0
[3, 3] = 1.0
[4, 4] = 1.0
[5, 5] = 1.0
可以使用 ``dense`` 或 ``full`` 函数做逆操作。 ``issparse`` 函数可用来检查矩阵是否稀疏:
.. doctest::
julia> issparse(speye(5))
true
稀疏矩阵运算
------------
稠密矩阵的算术运算也可以用在稀疏矩阵上。对稀疏矩阵进行赋值运算,是比较费资源的。大多数情况下,建议使用 ``findnz`` 函数把稀疏矩阵转换为 ``(I,J,V)`` 格式,在非零数或者稠密向量 ``(I,J,V)`` 的结构上做运算,最后再重构回稀疏矩阵。
稠密矩阵和稀疏矩阵函数对应关系
------------------------------
接下来的表格列出了内置的稀疏矩阵的函数, 及其对应的稠密矩阵的函数。通常,稀疏矩阵的函数,要么返回与输入稀疏矩阵 ``S`` 同样的稀疏度,要么返回 ``d`` 稠密度,例如矩阵的每个元素是非零的概率为 ``d`` 。
详见可以标准库文档的 :ref:`stdlib-sparse` 章节。
.. tabularcolumns:: |l|l|L|
+-----------------------+-------------------+----------------------------------------+
| 稀疏矩阵 | 稠密矩阵 | 说明 |
+-----------------------+-------------------+----------------------------------------+
| ``spzeros(m,n)`` | ``zeros(m,n)`` | 构造 *m* x *n* 的全 0 矩阵 |
| | | (``spzeros(m,n)`` 是空矩阵) |
+-----------------------+-------------------+----------------------------------------+
| ``spones(S)`` | ``ones(m,n)`` | 构造的全 1 矩阵 |
| | | 与稠密版本的不同, ``spones`` 的稀疏 |
| | | 度与 *S* 相同 |
+-----------------------+-------------------+----------------------------------------+
| ``speye(n)`` | ``eye(n)`` | 构造 *m* x *n* 的单位矩阵 |
+-----------------------+-------------------+----------------------------------------+
| ``full(S)`` | ``sparse(A)`` | 转换为稀疏矩阵和稠密矩阵 |
+-----------------------+-------------------+----------------------------------------+
| ``sprand(m,n,d)`` | ``rand(m,n)`` | 构造 *m*-by-*n* 的随机矩阵(稠密度为 |
| | | *d* ) 独立同分布的非零元素在 [0, 1] |
| | | 内均匀分布 |
+-----------------------+-------------------+----------------------------------------+
| ``sprandn(m,n,d)`` | ``randn(m,n)`` | 构造 *m*-by-*n* 的随机矩阵(稠密度为 |
| | | *d* ) 独立同分布的非零元素满足标准正 |
| | | 态(高斯)分布 |
+-----------------------+-------------------+----------------------------------------+
| ``sprandn(m,n,d,X)`` | ``randn(m,n,X)`` | 构造 *m*-by-*n* 的随机矩阵(稠密度为 |
| | | *d* ) 独立同分布的非零元素满足 *X* 分 |
| | | 布。(需要 ``Distributions`` 扩展包) |
+-----------------------+-------------------+----------------------------------------+
| ``sprandbool(m,n,d)`` | ``randbool(m,n)`` | 构造 *m*-by-*n* 的随机矩阵(稠密度为 |
| | | *d* ) ,非零 ``Bool``元素的概率为 *d* |
| | | (``randbool`` 中 *d* =0.5 ) |
+-----------------------+-------------------+----------------------------------------+
================================================
FILE: manual/calling-c-and-fortran-code.rst
================================================
.. _man-calling-c-and-fortran-code:
************************
调用 C 和 Fortran 代码
************************
Julia 调用 C 和 Fortran 的函数,既简单又高效。
被调用的代码应该是共享库的格式。大多数 C 和 Fortran 库都已经被编译为共享库。如果自己使用 GCC (或 Clang )编译代码,需要添加 ``-shared`` 和 ``-fPIC`` 选项。Julia 调用这些库的开销与本地 C 语言相同。
调用共享库和函数时使用多元组形式: ``(:function, "library")`` 或 ``("function", "library")`` ,其中 ``function`` 是 C 的导出函数名, ``library`` 是共享库名。共享库依据名字来解析,路径由环境变量来确定,有时需要直接指明。
多元组内有时仅有函数名(仅 ``:function`` 或 ``"function"`` )。此时,函数名由当前进程解析。这种形式可以用来调用 C 库函数, Julia 运行时函数,及链接到 Julia 的应用中的函数。
使用 ``ccall`` 来生成库函数调用。 ``ccall`` 的参数如下:
1. (:function, "library") 多元组对儿(必须为常量,详见下面)
2. 返回类型,可以为任意的位类型,包括 ``Int32`` , ``Int64`` , ``Float64`` ,或者指向任意类型参数 ``T`` 的指针 ``Ptr{T}`` ,或者仅仅是指向无类型指针 ``void*`` 的 ``Ptr``
3. 输入的类型的多元组,与上述的返回类型的要求类似。输入必须是多元组,而不是值为多元组的变量或表达式
4. 后面的参数,如果有的话,都是被调用函数的实参
下例调用标准 C 库中的 ``clock`` : ::
julia> t = ccall( (:clock, "libc"), Int32, ())
2292761
julia> t
2292761
julia> typeof(ans)
Int32
``clock`` 函数没有参数,返回 ``Int32`` 类型。输入的类型如果只有一个,常写成一元多元组,在后面跟一逗号。例如要调用 ``getenv`` 函数取得指向一个环境变量的指针,应这样调用: ::
julia> path = ccall( (:getenv, "libc"), Ptr{Uint8}, (Ptr{Uint8},), "SHELL")
Ptr{Uint8} @0x00007fff5fbffc45
julia> bytestring(path)
"/bin/bash"
注意,类型多元组的参数必须写成 ``(Ptr{Uint8},)`` ,而不是 ``(Ptr{Uint8})`` 。这是因为 ``(Ptr{Uint8})`` 等价于 ``Ptr{Uint8}`` ,它并不是一个包含 ``Ptr{Uint8}`` 的一元多元组: ::
julia> (Ptr{Uint8})
Ptr{Uint8}
julia> (Ptr{Uint8},)
(Ptr{Uint8},)
实际中要提供可复用代码时,通常要使用 Julia 的函数来封装 ``ccall`` ,设置参数,然后检查 C 或 Fortran 函数中可能出现的任何错误,将其作为异常传递给 Julia 的函数调用者。下例中, ``getenv`` C 库函数被封装在 `env.jl <https://github.com/JuliaLang/julia/blob/master/base/env.jl>`_ 里的 Julia 函数中: ::
function getenv(var::String)
val = ccall( (:getenv, "libc"),
Ptr{Uint8}, (Ptr{Uint8},), var)
if val == C_NULL
error("getenv: undefined variable: ", var)
end
bytestring(val)
end
上例中,如果函数调用者试图读取一个不存在的环境变量,封装将抛出异常: ::
julia> getenv("SHELL")
"/bin/bash"
julia> getenv("FOOBAR")
getenv: undefined variable: FOOBAR
下例稍复杂些,显示本地机器的主机名: ::
function gethostname()
hostname = Array(Uint8, 128)
ccall( (:gethostname, "libc"), Int32,
(Ptr{Uint8}, Uint),
hostname, length(hostname))
return bytestring(convert(Ptr{Uint8}, hostname))
end
此例先分配出一个字节数组,然后调用 C 库函数 ``gethostname`` 向数组中填充主机名,取得指向主机名缓冲区的指针,在默认其为空结尾 C 字符串的前提下,将其转换为 Julia 字符串。 C 库函数一般都用这种方式从函数调用者那儿,将申请的内存传递给被调用者,然后填充。在 Julia 中分配内存,通常都需要通过构建非初始化数组,然后将指向数据的指针传递给 C 函数。
调用 Fortran 函数时,所有的输入都必须通过引用来传递。
``&`` 前缀说明传递的是指向标量参数的指针,而不是标量值本身。下例使用 BLAS 函数计算点积:
::
function compute_dot(DX::Vector{Float64}, DY::Vector{Float64})
assert(length(DX) == length(DY))
n = length(DX)
incx = incy = 1
product = ccall( (:ddot_, "libLAPACK"),
Float64,
(Ptr{Int32}, Ptr{Float64}, Ptr{Int32}, Ptr{Float64}, Ptr{Int32}),
&n, DX, &incx, DY, &incy)
return product
end
前缀 ``&`` 的意思与 C 中的不同。对引用的变量的任何更改,都是对 Julia 不可见的。 ``&`` 并不是真正的地址运算符,可以在任何语法中使用它,例如 ``&0`` 和 ``&f(x)`` 。
注意在处理过程中,C 的头文件可以放在任何地方。目前还不能将 Julia 的结构和其他非基础类型传递给 C 库。通过传递指针来生成、使用非透明结构类型的 C 函数,可以向 Julia 返回 ``Ptr{Void}`` 类型的值,这个值以 ``Ptr{Void}`` 的形式被其它 C 函数调用。可以像任何 C 程序一样,通过调用库中对应的程序,对对象进行内存分配和释放。
把 C 类型映射到 Julia
---------------------
Julia 自动调用 ``convert`` 函数,将参数转换为指定类型。例如: ::
ccall( (:foo, "libfoo"), Void, (Int32, Float64),
x, y)
会按如下操作: ::
ccall( (:foo, "libfoo"), Void, (Int32, Float64),
convert(Int32, x), convert(Float64, y))
如果标量值与 ``&`` 一起被传递作为 ``Ptr{T}`` 类型的参数时,值首先会被转换为 ``T`` 类型。
数组转换
~~~~~~~~
把数组作为一个 ``Ptr{T}`` 参数传递给 C 时,它不进行转换。Julia 仅检查元素类型是否为 ``T`` ,然后传递首元素的地址。这样做可以避免不必要的复制整个数组。
因此,如果 ``Array`` 中的数据格式不对时,要使用显式转换,如 ``int32(a)`` 。
如果想把数组 *不经转换* 而作为一个不同类型的指针传递时,要么声明参数为 ``Ptr{Void}`` 类型,要么显式调用 ``convert(Ptr{T}, pointer(A))`` 。
类型相关
~~~~~~~~
基础的 C/C++ 类型和 Julia 类型对照如下。每个 C 类型也有一个对应名称的 Julia 类型,不过冠以了前缀 C 。这有助于编写简便的代码(但 C 中的 int 与 Julia 中的 Int 不同)。
**与系统无关:**
+------------------------+-------------------+--------------------------------+
| ``unsigned char`` | ``Cuchar`` | ``Uint8`` |
+------------------------+-------------------+--------------------------------+
| ``short`` | ``Cshort`` | ``Int16`` |
+------------------------+-------------------+--------------------------------+
| ``unsigned short`` | ``Cushort`` | ``Uint16`` |
+------------------------+-------------------+--------------------------------+
| ``int`` | ``Cint`` | ``Int32`` |
+------------------------+-------------------+--------------------------------+
| ``unsigned int`` | ``Cuint`` | ``Uint32`` |
+------------------------+-------------------+--------------------------------+
| ``long long`` | ``Clonglong`` | ``Int64`` |
+------------------------+-------------------+--------------------------------+
| ``unsigned long long`` | ``Culonglong`` | ``Uint64`` |
+------------------------+-------------------+--------------------------------+
| ``intmax_t`` | ``Cintmax_t`` | ``Int64`` |
+------------------------+-------------------+--------------------------------+
| ``uintmax_t`` | ``Cuintmax_t`` | ``Uint64`` |
+------------------------+-------------------+--------------------------------+
| ``float`` | ``Cfloat`` | ``Float32`` |
+------------------------+-------------------+--------------------------------+
| ``double`` | ``Cdouble`` | ``Float64`` |
+------------------------+-------------------+--------------------------------+
| ``ptrdiff_t`` | ``Cptrdiff_t`` | ``Int`` |
+------------------------+-------------------+--------------------------------+
| ``ssize_t`` | ``Cssize_t`` | ``Int`` |
+------------------------+-------------------+--------------------------------+
| ``size_t`` | ``Csize_t`` | ``Uint`` |
+------------------------+-------------------+--------------------------------+
| ``void`` | | ``Void`` |
+------------------------+-------------------+--------------------------------+
| ``void*`` | | ``Ptr{Void}`` |
+------------------------+-------------------+--------------------------------+
| ``char*`` (or ``char[]``, e.g. a string) | ``Ptr{Uint8}`` |
+------------------------+-------------------+--------------------------------+
| ``char**`` (or ``*char[]``) | ``Ptr{Ptr{Uint8}}`` |
+------------------------+-------------------+--------------------------------+
| ``struct T*`` (where T represents an | ``Ptr{T}`` (call using |
| appropriately defined bits type) | &variable_name in the |
| | parameter list) |
+------------------------+-------------------+--------------------------------+
| ``struct T`` (where T represents an | ``T`` (call using |
| appropriately defined bits type) | &variable_name in the |
| | parameter list) |
+------------------------+-------------------+--------------------------------+
| ``jl_value_t*`` (any Julia Type) | ``Ptr{Any}`` |
+------------------------+-------------------+--------------------------------+
Julia 的 ``Char`` 类型是 32 位的,与所有平台的宽字符类型 (``wchar_t`` 或 ``wint_t``) 不同。
返回 ``void`` 的 C 函数,在 Julia 中返回 ``nothing`` 。
**与系统有关:**
====================== ============== =======
``char`` ``Cchar`` ``Int8`` (x86, x86_64)
``Uint8`` (powerpc, arm)
``long`` ``Clong`` ``Int`` (UNIX)
``Int32`` (Windows)
``unsigned long`` ``Culong`` ``Uint`` (UNIX)
``Uint32`` (Windows)
``wchar_t`` ``Cwchar_t`` ``Int32`` (UNIX)
``Uint16`` (Windows)
====================== ============== =======
对应于字符串参数( ``char*`` )的 Julia 类型为 ``Ptr{Uint8}`` ,而不是 ``ASCIIString`` 。参数中有 ``char**`` 类型的 C 函数,在 Julia 中调用时应使用 ``Ptr{Ptr{Uint8}}`` 类型。例如,C 函数: ::
int main(int argc, char **argv);
在 Julia 中应该这样调用: ::
argv = [ "a.out", "arg1", "arg2" ]
ccall(:main, Int32, (Int32, Ptr{Ptr{Uint8}}), length(argv), argv)
For ``wchar_t*`` arguments, the Julia type should be ``Ptr{Wchar_t}``,
and data can be converted to/from ordinary Julia strings by the
``wstring(s)`` function (equivalent to either ``utf16(s)`` or ``utf32(s)``
depending upon the width of ``Cwchar_t``. Note also that ASCII, UTF-8,
UTF-16, and UTF-32 string data in Julia is internally NUL-terminated, so
it can be passed to C functions expecting NUL-terminated data without making
a copy.
通过指针读取数据
----------------
下列方法是“不安全”的,因为坏指针或类型声明可能会导致意外终止或损坏任意进程内存。
指定 ``Ptr{T}`` ,常使用 ``unsafe_ref(ptr, [index])`` 方法,将类型为 ``T`` 的内容从所引用的内存复制到 Julia 对象中。 ``index`` 参数是可选的(默认为 1 ),它是从 1 开始的索引值。此函数类似于 ``getindex()`` 和 ``setindex!()`` 的行为(如 ``[]`` 语法)。
返回值是一个被初始化的新对象,它包含被引用内存内容的浅拷贝。被引用的内存可安全释放。
如果 ``T`` 是 ``Any`` 类型,被引用的内存会被认为包含对 Julia 对象 ``jl_value_t*`` 的引用,结果为这个对象的引用,且此对象不会被拷贝。需要谨慎确保对象始终对垃圾回收机制可见(指针不重要,重要的是新的引用),来确保内存不会过早释放。注意,如果内存原本不是由 Julia 申请的,新对象将永远不会被 Julia 的垃圾回收机制释放。如果 ``Ptr`` 本身就是 ``jl_value_t*`` ,可使用 ``unsafe_pointer_to_objref(ptr)`` 将其转换回 Julia 对象引用。(可通过调用 ``pointer_from_objref(v)`` 将Julia 值 ``v`` 转换为 ``jl_value_t*`` 指针 ``Ptr{Void}`` 。)
逆操作(向 Ptr{T} 写数据)可通过 ``unsafe_store!(ptr, value, [index])`` 来实现。目前,仅支持位类型和其它无指针( ``isbits`` )不可变类型。
现在任何抛出异常的操作,估摸着都是还没实现完呢。来写个帖子上报 bug 吧,就会有人来解决啦。
如果所关注的指针是(位类型或不可变)的目标数据数组, ``pointer_to_array(ptr,dims,[own])`` 函数就非常有用啦。如果想要 Julia “控制”底层缓冲区并在返回的 ``Array`` 被释放时调用 ``free(ptr)`` ,最后一个参数应该为真。如果省略 ``own`` 参数或它为假,则调用者需确保缓冲区一直存在,直至所有的读取都结束。
``Ptr`` 的算术(比如 ``+``) 和 C 的指针算术不同, 对 ``Ptr`` 加一个整数会将指针移动一段距离的 *字节* , 而不是元素。这样从指针运算上得到的地址不会依赖指针类型。
.. Arithmetic on the ``Ptr`` type in Julia (e.g. using ``+``) does not behave the
.. same as C's pointer arithmetic. Adding an integer to a ``Ptr`` in Julia always
.. moves the pointer by some number of *bytes*, not elements. This way, the
.. address values obtained from pointer arithmetic do not depend on the
.. element types of pointers.
用指针传递修改值
-------------------------------------
.. Passing Pointers for Modifying Inputs
.. -------------------------------------
.. Because C doesn't support multiple return values, often C functions will take
.. pointers to data that the function will modify. To accomplish this within a
.. ``ccall`` you need to encapsulate the value inside an array of the appropriate
.. type. When you pass the array as an argument with a ``Ptr`` type, julia will
.. automatically pass a C pointer to the encapsulated data
因为 C 不支持多返回值, 所以通常 C 函数会用指针来修改值。 在 ``ccall`` 里完成这些需要把值放在适当类型的数组里。当你用 ``Ptr`` 传递整个数组时,
Julia 会自动传递一个 C 指针到被这个值::
width = Cint[0]
range = Cfloat[0]
ccall(:foo, Void, (Ptr{Cint}, Ptr{Cfloat}), width, range)
这被广泛用在了 Julia 的 LAPACK 接口上, 其中整数类型的 ``info`` 被以引用的方式传到 LAPACK, 再返回是否成功。
.. readproof
.. This is used extensively in Julia's LAPACK interface, where an integer ``info``
.. is passed to LAPACK by reference, and on return, includes the success code.
垃圾回收机制的安全
------------------
给 ccall 传递数据时,最好避免使用 ``pointer()`` 函数。应当定义一个转换方法,将变量直接传递给 ccall 。ccall 会自动安排,使得在调用返回前,它的所有参数都不会被垃圾回收机制处理。如果 C API 要存储一个由 Julia 分配好的内存的引用,当 ccall 返回后,需要自己设置,使对象对垃圾回收机制保持可见。推荐的方法为,在一个类型为 ``Array{Any,1}`` 的全局变量中保存这些值,直到 C 接口通知它已经处理完了。
只要构造了指向 Julia 数据的指针,就必须保证原始数据直至指针使用完之前一直存在。Julia 中的许多方法,如 ``unsafe_ref()`` 和 ``bytestring()`` ,都复制数据而不是控制缓冲区,因此可以安全释放(或修改)原始数据,不会影响到 Julia 。有一个例外需要注意,由于性能的原因, ``pointer_to_array()`` 会共享(或控制)底层缓冲区。
垃圾回收并不能保证回收的顺序。例如,当 ``a`` 包含对 ``b`` 的引用,且两者都要被垃圾回收时,不能保证 ``b`` 在 ``a`` 之后被回收。这需要用其它方式来处理。
非常量函数说明
--------------
``(name, library)`` 函数说明应为常量表达式。可以通过 ``eval`` ,将计算结果作为函数名: ::
@eval ccall(($(string("a","b")),"lib"), ...
表达式用 ``string`` 构造名字,然后将名字代入 ``ccall`` 表达式进行计算。注意 ``eval`` 仅在顶层运行,因此在表达式之内,不能使用本地变量(除非本地变量的值使用 ``$`` 进行过内插)。 ``eval`` 通常用来作为顶层定义,例如,将包含多个相似函数的库封装在一起。
间接调用
--------
``ccall`` 的第一个参数可以是运行时求值的表达式。此时,表达式的值应为 ``Ptr`` 类型,指向要调用的原生函数的地址。这个特性用于 ``ccall``
的第一参数包含对非常量(本地变量或函数参数)的引用时。
调用方式
--------
``ccall`` 的第二个(可选)参数指定调用方式(在返回值之前)。如果没指定,将会使用操作系统的默认 C 调用方式。其它支持的调用方式为: ``stdcall`` , ``cdecl`` , ``fastcall`` 和 ``thiscall`` 。例如 (来自 base/libc.jl): ::
hn = Array(Uint8, 256)
err=ccall(:gethostname, stdcall, Int32, (Ptr{Uint8}, Uint32), hn, length(hn))
更多信息请参考 `LLVM Language Reference`_.
.. _LLVM Language Reference: http://llvm.org/docs/LangRef.html#calling-conventions
Accessing Global Variables
--------------------------
Global variables exported by native libraries can be accessed by name using the
``cglobal`` function. The arguments to ``cglobal`` are a symbol specification
identical to that used by ``ccall``, and a type describing the value stored in
the variable::
julia> cglobal((:errno,:libc), Int32)
Ptr{Int32} @0x00007f418d0816b8
The result is a pointer giving the address of the value. The value can be
manipulated through this pointer using ``unsafe_load`` and ``unsafe_store``.
Passing Julia Callback Functions to C
-------------------------------------
It is possible to pass Julia functions to native functions that accept function
pointer arguments. A classic example is the standard C library ``qsort`` function,
declared as::
void qsort(void *base, size_t nmemb, size_t size,
int(*compare)(const void *a, const void *b));
The ``base`` argument is a pointer to an array of length ``nmemb``, with elements of
``size`` bytes each. ``compare`` is a callback function which takes pointers to two
elements ``a`` and ``b`` and returns an integer less/greater than zero if ``a`` should
appear before/after ``b`` (or zero if any order is permitted). Now, suppose that we
have a 1d array ``A`` of values in Julia that we want to sort using the ``qsort``
function (rather than Julia’s built-in sort function). Before we worry about calling
``qsort`` and passing arguments, we need to write a comparison function that works for
some arbitrary type T::
function mycompare{T}(a_::Ptr{T}, b_::Ptr{T})
a = unsafe_load(a_)
b = unsafe_load(b_)
return convert(Cint, a < b ? -1 : a > b ? +1 : 0)
end
Notice that we have to be careful about the return type: ``qsort`` expects a function
returning a C ``int``, so we must be sure to return ``Cint`` via a call to ``convert``.
In order to pass this function to C, we obtain its address using the function
``cfunction``::
const mycompare_c = cfunction(mycompare, Cint, (Ptr{Cdouble}, Ptr{Cdouble}))
``cfunction`` accepts three arguments: the Julia function (``mycompare``), the return
type (``Cint``), and a tuple of the argument types, in this case to sort an array of
``Cdouble`` (Float64) elements.
The final call to ``qsort`` looks like this::
A = [1.3, -2.7, 4.4, 3.1]
ccall(:qsort, Void, (Ptr{Cdouble}, Csize_t, Csize_t, Ptr{Void}),
A, length(A), sizeof(eltype(A)), mycompare_c)
After this executes, ``A`` is changed to the sorted array ``[ -2.7, 1.3, 3.1, 4.4]``.
Note that Julia knows how to convert an array into a ``Ptr{Cdouble}``, how to compute
the size of a type in bytes (identical to C’s ``sizeof`` operator), and so on.
For fun, try inserting a ``println("mycompare($a,$b)")`` line into ``mycompare``, which
will allow you to see the comparisons that ``qsort`` is performing (and to verify that
it is really calling the Julia function that you passed to it).
Thread-safety
~~~~~~~~~~~~~
Some C libraries execute their callbacks from a different thread, and
since Julia isn't thread-safe you'll need to take some extra
precautions. In particular, you'll need to set up a two-layered
system: the C callback should only *schedule* (via Julia's event loop)
the execution of your "real" callback. Your callback
needs to be written to take two inputs (which you'll most likely just
discard) and then wrapped by ``SingleAsyncWork``::
cb = Base.SingleAsyncWork(data -> my_real_callback(args))
The callback you pass to C should only execute a ``ccall`` to
``:uv_async_send``, passing ``cb.handle`` as the argument.
More About Callbacks
~~~~~~~~~~~~~~~~~~~~
For more details on how to pass callbacks to C libraries, see this
`blog post <http://julialang.org/blog/2013/05/callback/>`_.
C++
---
`Cpp <https://github.com/timholy/Cpp.jl>`_ 和 `Clang <https://github.com/ihnorton/Clang.jl>`_ 扩展包提供了有限的 C++ 支持。
处理不同平台
------------
当处理不同的平台库的时候,经常要针对特殊平台提供特殊函数。这时常用到变量 ``OS_NAME`` 。此外,还有一些常用的宏: ``@windows``, ``@unix``, ``@linux``, 及 ``@osx`` 。注意, linux 和 osx 是 unix 的不相交的子集。宏的用法类似于三元条件运算符。
简单的调用: ::
ccall( (@windows? :_fopen : :fopen), ...)
复杂的调用: ::
@linux? (
begin
some_complicated_thing(a)
end
: begin
some_different_thing(a)
end
)
链式调用(圆括号可以省略,但为了可读性,最好加上): ::
@windows? :a : (@osx? :b : :c)
================================================
FILE: manual/complex-and-rational-numbers.md
================================================
# 复数与分数
<!-- # Complex and Rational Numbers -->
Julia 语言自带预定义的表示复数与分数的类型,并且支持它们的各种[标准数学操作与基础函数](@ref)。由于也定义了复数与分数的[转换与提升](@ref conversion-and-promotion),在预定义数字类型(无论是原始的还是复合的)的任意组合上的操作都会像预期一样表现。
<!-- Julia ships with predefined types representing both complex and rational numbers, and supports
all standard [Mathematical Operations and Elementary Functions](@ref) on them. [Conversion and Promotion](@ref conversion-and-promotion) are defined
so that operations on any combination of predefined numeric types, whether primitive or composite,
behave as expected. -->
## 复数
<!-- ## Complex Numbers -->
全局常量 [`im`](@ref) 绑定到复数 *i*,表示 -1 的主平方根。由于 `i` 是一个很流行的用作索引的变量名,所以直接把它作为全局常量被认为是很危险的。由于 Julia 允许数字常量能[作为系数与标识符并置](@ref man-numeric-literal-coefficients),这种绑定就足够为复数提供很方便的语法,类似于传统的数学记法:
<!-- The global constant [`im`](@ref) is bound to the complex number *i*, representing the principal
square root of -1. It was deemed harmful to co-opt the name `i` for a global constant, since it
is such a popular index variable name. Since Julia allows numeric literals to be [juxtaposed with identifiers as coefficients](@ref man-numeric-literal-coefficients),
this binding suffices to provide convenient syntax for complex numbers, similar to the traditional
mathematical notation: -->
```jldoctest
julia> 1 + 2im
1 + 2im
```
你可以在复数上进行各种标准算术操作:
<!-- You can perform all the standard arithmetic operations with complex numbers: -->
```jldoctest
julia> (1 + 2im)*(2 - 3im)
8 + 1im
julia> (1 + 2im)/(1 - 2im)
-0.6 + 0.8im
julia> (1 + 2im) + (1 - 2im)
2 + 0im
julia> (-3 + 2im) - (5 - 1im)
-8 + 3im
julia> (-1 + 2im)^2
-3 - 4im
julia> (-1 + 2im)^2.5
2.7296244647840084 - 6.960664459571898im
julia> (-1 + 2im)^(1 + 1im)
-0.27910381075826657 + 0.08708053414102428im
julia> 3(2 - 5im)
6 - 15im
julia> 3(2 - 5im)^2
-63 - 60im
julia> 3(2 - 5im)^-1.0
0.20689655172413796 + 0.5172413793103449im
```
类型提升机制确保了也可以使用不同类型的操作数的组合:
<!-- The promotion mechanism ensures that combinations of operands of different types just work: -->
```jldoctest
julia> 2(1 - 1im)
2 - 2im
julia> (2 + 3im) - 1
1 + 3im
julia> (1 + 2im) + 0.5
1.5 + 2.0im
julia> (2 + 3im) - 0.5im
2.0 + 2.5im
julia> 0.75(1 + 2im)
0.75 + 1.5im
julia> (2 + 3im) / 2
1.0 + 1.5im
julia> (1 - 3im) / (2 + 2im)
-0.5 - 1.0im
julia> 2im^2
-2 + 0im
julia> 1 + 3/4im
1.0 - 0.75im
```
注意 `3/4im == 3/(4*im) == -(3/4*im)`,因为字面量系数比除法的优先级更高。
<!-- Note that `3/4im == 3/(4*im) == -(3/4*im)`, since a literal coefficient binds more tightly than
division. -->
Julia 提供了一些用来操作复数值的标准函数:
<!-- Standard functions to manipulate complex values are provided: -->
```jldoctest
julia> z = 1 + 2im
1 + 2im
julia> real(1 + 2im) # real part of z
1
julia> imag(1 + 2im) # imaginary part of z
2
julia> conj(1 + 2im) # complex conjugate of z
1 - 2im
julia> abs(1 + 2im) # absolute value of z
2.23606797749979
julia> abs2(1 + 2im) # squared absolute value
5
julia> angle(1 + 2im) # phase angle in radians
1.1071487177940904
```
与往常一样,复数的绝对值([`abs`](@ref))是从零点到它的距离。[`abs2`](@ref) 提供了绝对值的平方,在复数上使用可以避免做平方根的操作,所以有时候特别有用。[`angle`] 返回以弧度为单位的相位角(这也被称为辐角函数)。所有其它的[基础函数](@ref)也都在复数上有完整的定义:
<!-- As usual, the absolute value ([`abs`](@ref)) of a complex number is its distance from zero.
[`abs2`](@ref) gives the square of the absolute value, and is of particular use for complex
numbers where it avoids taking a square root. [`angle`](@ref) returns the phase angle in radians
(also known as the *argument* or *arg* function). The full gamut of other [Elementary Functions](@ref)
is also defined for complex numbers: -->
```jldoctest
julia> sqrt(1im)
0.7071067811865476 + 0.7071067811865475im
julia> sqrt(1 + 2im)
1.272019649514069 + 0.7861513777574233im
julia> cos(1 + 2im)
2.0327230070196656 - 3.0518977991518im
julia> exp(1 + 2im)
-1.1312043837568135 + 2.4717266720048188im
julia> sinh(1 + 2im)
-0.4890562590412937 + 1.4031192506220405im
```
注意数学函数通常应用于实数就返回实数值,应用于复数就返回复数值。例如,当 [`sqrt`](@ref) 应用于 `-1` 与 `-1 + 0im` 会有不同的表现,虽然 `-1 == -1 + 0im`:
<!-- Note that mathematical functions typically return real values when applied to real numbers and
complex values when applied to complex numbers. For example, [`sqrt`](@ref) behaves differently
when applied to `-1` versus `-1 + 0im` even though `-1 == -1 + 0im`: -->
```jldoctest
julia> sqrt(-1)
ERROR: DomainError with -1.0:
sqrt will only return a complex result if called with a complex argument. Try sqrt(Complex(x)).
Stacktrace:
[...]
julia> sqrt(-1 + 0im)
0.0 + 1.0im
```
从变量构建复数时不支持[字面量数值参数记法](@ref man-numeric-literal-coefficients),乘法必须显式地写出:
<!-- The [literal numeric coefficient notation](@ref man-numeric-literal-coefficients) does not work when constructing a complex number
from variables. Instead, the multiplication must be explicitly written out: -->
```jldoctest
julia> a = 1; b = 2; a + b*im
1 + 2im
```
然而,这**并不**被推荐。使用 [`complex`](@ref) 函数可以直接通过实部与虚部构建一个复数值:
<!-- However, this is *not* recommended; Use the [`complex`](@ref) function instead to construct
a complex value directly from its real and imaginary parts: -->
```jldoctest
julia> a = 1; b = 2; complex(a, b)
1 + 2im
```
这种构建避免了乘法和加法操作。
<!-- This construction avoids the multiplication and addition operations. -->
[`Inf`](@ref) 和 [`NaN`](@ref) 可能出现在复数的实部和虚部,正如[特殊的浮点值](@ref)章节所描述的:
<!-- [`Inf`](@ref) and [`NaN`](@ref) propagate through complex numbers in the real and imaginary parts
of a complex number as described in the [Special floating-point values](@ref) section: -->
```jldoctest
julia> 1 + Inf*im
1.0 + Inf*im
julia> 1 + NaN*im
1.0 + NaN*im
```
## 分数
<!-- ## Rational Numbers -->
Julia 有一个用于表示整数精准的比例的分数类型。分数通过 [`//`](@ref) 运算符构建:
<!-- Julia has a rational number type to represent exact ratios of integers. Rationals are constructed
using the [`//`](@ref) operator: -->
```jldoctest
julia> 2//3
2//3
```
如果一个分数的分子和分母含有公因子,它们会被约分到最简形式且分母非负:
<!-- If the numerator and denominator of a rational have common factors, they are reduced to lowest
terms such that the denominator is non-negative: -->
```jldoctest
julia> 6//9
2//3
julia> -4//8
-1//2
julia> 5//-15
-1//3
julia> -4//-12
1//3
```
整数比值的这种标准化形式时唯一的,所以分数的相等性可由校验分子与分母的相等性来测试。分数标准化的分子和分母可以使用 [`numerator`](@ref) 和 [`denominator`](@ref) 函数得到:
<!-- This normalized form for a ratio of integers is unique, so equality of rational values can be
tested by checking for equality of the numerator and denominator. The standardized numerator and
denominator of a rational value can be extracted using the [`numerator`](@ref) and [`denominator`](@ref)
functions: -->
```jldoctest
julia> numerator(2//3)
2
julia> denominator(2//3)
3
```
分子和分母的直接比较通常是不必要的,因为标准算术和比较操作对分数值也有定义:
<!-- Direct comparison of the numerator and denominator is generally not necessary, since the standard
arithmetic and comparison operations are defined for rational values: -->
```jldoctest
julia> 2//3 == 6//9
true
julia> 2//3 == 9//27
false
julia> 3//7 < 1//2
true
julia> 3//4 > 2//3
true
julia> 2//4 + 1//6
2//3
julia> 5//12 - 1//4
1//6
julia> 5//8 * 3//12
5//32
julia> 6//5 / 10//7
21//25
```
分数可以很容易地被转换成浮点数:
<!-- Rationals can be easily converted to floating-point numbers: -->
```jldoctest
julia> float(3//4)
0.75
```
对 `a` 和 `b` 的任意整数值(除了 `a == 0` 且 `b == 0` 时),从分数到浮点数的转换遵从了以下的一致性:
<!-- Conversion from rational to floating-point respects the following identity for any integral values
of `a` and `b`, with the exception of the case `a == 0` and `b == 0`: -->
```jldoctest
julia> a = 1; b = 2;
julia> isequal(float(a//b), a/b)
true
```
对无穷分数值的构建也是可接受的:
<!-- Constructing infinite rational values is acceptable: -->
```jldoctest
julia> 5//0
1//0
julia> -3//0
-1//0
julia> typeof(ans)
Rational{Int64}
```
然而,试图构建一个 [`NaN`](@ref) 分数值不是:
<!-- Trying to construct a [`NaN`](@ref) rational value, however, is not: -->
```jldoctest
julia> 0//0
ERROR: ArgumentError: invalid rational: zero(Int64)//zero(Int64)
Stacktrace:
[...]
```
与往常一样,类型提升系统使得分数可以轻松地和其它数值类型进行相互作用:
<!-- As usual, the promotion system makes interactions with other numeric types effortless: -->
```jldoctest
julia> 3//5 + 1
8//5
julia> 3//5 - 0.5
0.09999999999999998
julia> 2//7 * (1 + 2im)
2//7 + 4//7*im
julia> 2//7 * (1.5 + 2im)
0.42857142857142855 + 0.5714285714285714im
julia> 3//2 / (1 + 2im)
3//10 - 3//5*im
julia> 1//2 + 2im
1//2 + 2//1*im
julia> 1 + 2//3im
1//1 - 2//3*im
julia> 0.5 == 1//2
true
julia> 0.33 == 1//3
false
julia> 0.33 < 1//3
true
julia> 1//3 - 0.33
0.0033333333333332993
```
================================================
FILE: manual/complex-and-rational-numbers.rst
================================================
.. _man-complex-and-rational-numbers:
************
复数和分数
************
Julia 提供复数和分数类型,并对其支持所有的 :ref:`标准数学运算 <man-mathematical-operations>` 。对不同的数据类型进行混合运算时,无论是基础的还是复合的,都会自动使用 :ref:`man-conversion-and-promotion` .
.. _man-complex-numbers:
复数
----
全局变量 ``im`` 即复数 *i* ,表示 -1 的正平方根。因为 ``i`` 经常作为索引变量,所以不使用它来代表复数了。Julia 允许数值文本作为 :ref:`代数系数 <man-numeric-literal-coefficients>` ,也适用于复数:
.. doctest::
julia> 1 + 2im
1 + 2im
可以对复数做标准算术运算:
.. doctest::
julia> (1 + 2im)*(2 - 3im)
8 + 1im
julia> (1 + 2im)/(1 - 2im)
-0.6 + 0.8im
julia> (1 + 2im) + (1 - 2im)
2 + 0im
julia> (-3 + 2im) - (5 - 1im)
-8 + 3im
julia> (-1 + 2im)^2
-3 - 4im
julia> (-1 + 2im)^2.5
2.7296244647840084 - 6.960664459571898im
julia> (-1 + 2im)^(1 + 1im)
-0.27910381075826657 + 0.08708053414102428im
julia> 3(2 - 5im)
6 - 15im
julia> 3(2 - 5im)^2
-63 - 60im
julia> 3(2 - 5im)^-1.0
0.20689655172413796 + 0.5172413793103449im
类型提升机制保证了不同类型的运算对象能够在一起运算:
.. doctest::
julia> 2(1 - 1im)
2 - 2im
julia> (2 + 3im) - 1
1 + 3im
julia> (1 + 2im) + 0.5
1.5 + 2.0im
julia> (2 + 3im) - 0.5im
2.0 + 2.5im
julia> 0.75(1 + 2im)
0.75 + 1.5im
julia> (2 + 3im) / 2
1.0 + 1.5im
julia> (1 - 3im) / (2 + 2im)
-0.5 - 1.0im
julia> 2im^2
-2 + 0im
julia> 1 + 3/4im
1.0 - 0.75im
注意: ``3/4im == 3/(4*im) == -(3/4*im)`` ,因为文本系数比除法优先。
处理复数的标准函数:
.. doctest::
julia> real(1 + 2im)
1
julia> imag(1 + 2im)
2
julia> conj(1 + 2im)
1 - 2im
julia> abs(1 + 2im)
2.23606797749979
julia> abs2(1 + 2im)
5
julia> angle(1 + 2im)
1.1071487177940904
.. As usual, the absolute value (``abs``) of a complex number is its
.. distance from zero. The ``abs2`` function gives the square of the
.. absolute value, and is of particular use for complex numbers where it
.. avoids taking a square root. The ``angle`` function returns the phase
.. angle in radians (also known as the *argument* or *arg* function).
通常, 复数的绝对值( ``abs`` )是它到零的距离。 函数 ``abs2`` 返回绝对值的平方,
特别地用在复数上来避免开根。``angle`` 函数返回弧度制的相位(即 *argument* 或 *arg* )。
所有的 :ref:`man-elementary-functions` 也可以应用在复数上:
.. doctest::
julia> sqrt(1im)
0.7071067811865476 + 0.7071067811865475im
julia> sqrt(1 + 2im)
1.272019649514069 + 0.7861513777574233im
julia> cos(1 + 2im)
2.0327230070196656 - 3.0518977991518im
julia> exp(1 + 2im)
-1.1312043837568135 + 2.4717266720048188im
julia> sinh(1 + 2im)
-0.4890562590412937 + 1.4031192506220405im
作用在实数上的数学函数,返回值一般为实数;作用在复数上的,返回值为复数。例如, ``sqrt`` 对 ``-1`` 和 ``-1 + 0im`` 的结果不同,即使 ``-1 == -1 + 0im`` :
.. doctest::
julia> sqrt(-1)
ERROR: DomainError
sqrt will only return a complex result if called with a complex argument.
try sqrt(complex(x))
in sqrt at math.jl:131
julia> sqrt(-1 + 0im)
0.0 + 1.0im
:ref:`代数系数 <man-numeric-literal-coefficients>` 不能用于使用变量构造复数。乘法必须显式的写出来:
.. doctest::
julia> a = 1; b = 2; a + b*im
1 + 2im
但是, *不* 推荐使用上面的方法。推荐使用 ``complex`` 函数构造复数:
.. doctest::
julia> complex(a,b)
1 + 2im
这种构造方式避免了乘法和加法操作。
``Inf`` 和 ``NaN`` 也可以参与构造复数 (参考 :ref:`man-special-floats` 部分):
.. doctest::
julia> 1 + Inf*im
1.0 + Inf*im
julia> 1 + NaN*im
1.0 + NaN*im
.. _man-rational-numbers:
分数
----
Julia 有分数类型。使用 ``//`` 运算符构造分数:
.. doctest::
julia> 2//3
2//3
如果分子、分母有公约数,将自动约简至最简分数,且分母为非负数:
.. doctest::
julia> 6//9
2//3
julia> -4//8
-1//2
julia> 5//-15
-1//3
julia> -4//-12
1//3
约简后的分数都是唯一的,可以通过分别比较分子、分母来确定两个分数是否相等。使用 ``num`` 和 ``den`` 函数来取得约简后的分子和分母:
.. doctest::
julia> num(2//3)
2
julia> den(2//3)
3
其实并不需要比较分数和分母,我们已经为分数定义了标准算术和比较运算:
.. doctest::
julia> 2//3 == 6//9
true
julia> 2//3 == 9//27
false
julia> 3//7 < 1//2
true
julia> 3//4 > 2//3
true
julia> 2//4 + 1//6
2//3
julia> 5//12 - 1//4
1//6
julia> 5//8 * 3//12
5//32
julia> 6//5 / 10//7
21//25
分数可以简单地转换为浮点数:
.. doctest::
julia> float(3//4)
0.75
分数到浮点数的转换遵循,对任意整数 ``a`` 和 ``b`` ,除 ``a == 0`` 及 ``b == 0`` 之外,有:
.. doctest::
julia> isequal(float(a//b), a/b)
true
可以构造结果为 ``Inf`` 的分数:
.. doctest::
julia> 5//0
1//0
julia> -3//0
-1//0
julia> typeof(ans)
Rational{Int64} (constructor with 1 method)
但不能构造结果为 ``NaN`` 的分数:
.. doctest::
julia> 0//0
ERROR: invalid rational: 0//0
in Rational at rational.jl:6
in // at rational.jl:15
类型提升系统使得分数类型与其它数值类型交互非常简单:
.. doctest::
julia> 3//5 + 1
8//5
julia> 3//5 - 0.5
0.09999999999999998
julia> 2//7 * (1 + 2im)
2//7 + 4//7*im
julia> 2//7 * (1.5 + 2im)
0.42857142857142855 + 0.5714285714285714im
julia> 3//2 / (1 + 2im)
3//10 - 3//5*im
julia> 1//2 + 2im
1//2 + 2//1*im
julia> 1 + 2//3im
1//1 - 2//3*im
julia> 0.5 == 1//2
true
julia> 0.33 == 1//3
false
julia> 0.33 < 1//3
true
julia> 1//3 - 0.33
0.0033333333333332993
================================================
FILE: manual/constructors.rst
================================================
.. _man-constructors:
**********
构造函数
**********
构造函数 [#]_ 是构造新对象,即新 :ref:`man-composite-types` 实例的函数。在 Julia 中,
类型本身同时也可以作为构造函数使用:将他们作为函数调用可以构造相应类型的实例。这在引入复合类型
的时候已经有过简单的介绍。例如: ::
type Foo
bar
baz
end
julia> foo = Foo(1,2)
Foo(1,2)
julia> foo.bar
1
julia> foo.baz
2
对于很多类型来说,构造新对象唯一需要的是对各个域赋值。不过,构造新对象也可能会需要更复杂的操作。
为了保证特定的不变性,我们可能需要对参数进行检查或变换。 `递归数据结构 <http://en.wikipedia.org/wiki/Recursion_%28computer_science%29#Recursive_data_structures_.28structural_recursion.29>`_ ,尤其是自引用的数据结构,常需要先构造为非完整状态,再按步骤将其完善。我们有时也可能希望用更少或不同类型的参数更方便的构造对象。Julia 的构造函数可以让包括这些在内的各种需求得到满足。
.. [#] 关于命名:尽管“构造函数”通常被用来描述创建新对象的函数,它也经常被滥用于特定的构造方法。通常情况下,可以很容易地从上下文推断出到底是“构造函数”还是“构造方法”。
外部构造方法
------------
构造函数与 Julia 中的其它函数一样,它的行为取决于它全部方法的行为的组合。因此,你可以通过定义新方法来给构造函数增加新性能。下例给 ``Foo`` 添加了新构造方法,仅输入一个参数,将该参数值赋给 ``bar`` 和 ``baz`` 域: ::
Foo(x) = Foo(x,x)
julia> Foo(1)
Foo(1,1)
添加 ``Foo`` 的零参构造方法,给 ``bar`` 和 ``baz`` 域赋默认值: ::
Foo() = Foo(0)
julia> Foo()
Foo(0,0)
这种追加的构造方法被称为 *外部* 构造方法。它仅能通过提供默认值的方式,调用其它构造方法来构造实例。
内部构造方法
------------
*内部* 构造方法与外部构造方法类似,但有两个区别:
1. 它在类型声明块内部被声明,而不是像普通方法一样在外部被声明
2. 它调用本地已存在的 ``new`` 函数,来构造声明块的类型的对象
例如,要声明一个保存实数对的类型,且第一个数不大于第二个数:
.. testcode::
type OrderedPair
x::Real
y::Real
OrderedPair(x,y) = x > y ? error("out of order") : new(x,y)
end
仅当 ``x <= y`` 时,才会构造 ``OrderedPair`` 对象:
.. doctest::
julia> OrderedPair(1,2)
OrderedPair(1,2)
julia> OrderedPair(2,1)
ERROR: out of order
in OrderedPair at none:5
所有的外部构造方法,最终都会调用内部构造方法。
当然,如果类型被声明为 ``immutable`` ,它的构造函数的结构就不能变了。这对判断一个类型是否应该是 immutable 时很重要。
如果定义了内部构造方法,Julia 将不再提供默认的构造方法。默认的构造方法等价于一个自定义内部构造方法,它将对象的所有域作为参数(如果对应域有类型,应为具体类型),传递给 ``new`` ,最后返回结果对象: ::
type Foo
bar
baz
Foo(bar,baz) = new(bar,baz)
end
这个声明与前面未指明内部构造方法的 ``Foo`` 是等价的。下面两者也是等价的,一个使用默认构造方法,一个写明了构造方法: ::
type T1
x::Int64
end
type T2
x::Int64
T2(x) = new(x)
end
julia> T1(1)
T1(1)
julia> T2(1)
T2(1)
julia> T1(1.0)
T1(1)
julia> T2(1.0)
T2(1)
内部构造方法能不写就不写。提供默认值之类的事儿,应该写成外部构造方法,由它们调用内部构造方法。
部分初始化
----------
考虑如下递归类型声明: ::
type SelfReferential
obj::SelfReferential
end
如果 ``a`` 是 ``SelfReferential`` 的实例,则可以如下构造第二个实例: ::
b = SelfReferential(a)
但是,当没有任何实例来为 ``obj`` 域提供有效值时,如何构造第一个实例呢?唯一的解决方法是构造 ``obj`` 域未赋值的 ``SelfReferential`` 部分初始化实例,使用这个实例作为另一个实例(如它本身)中 ``obj`` 域的有效值。
构造部分初始化对象时,Julia 允许调用 ``new`` 函数来处理比该类型域个数少的参数,返回部分域未初始化的对象。这时,内部构造函数可以使用这个不完整的对象,并在返回之前完成它的初始化。下例中,我们定义 ``SelfReferential`` 类型时,使用零参内部构造方法,返回一个 ``obj`` 域指向它本身的实例:
.. testcode::
type SelfReferential
obj::SelfReferential
SelfReferential() = (x = new(); x.obj = x)
end
此构造方法可以运行并构造自引对象:
.. doctest::
julia> x = SelfReferential();
julia> is(x, x)
true
julia> is(x, x.obj)
true
julia> is(x, x.obj.obj)
true
内部构造方法最好返回完全初始化的对象,但也可以返回部分初始化对象:
.. doctest::
julia> type Incomplete
xx
Incomplete() = new()
end
julia> z = Incomplete();
尽管可以构造未初始化域的对象,但读取未初始化的引用会报错:
.. doctest::
julia> z.xx
ERROR: access to undefined reference
.. This avoids the need to continually check for ``null`` values.
.. However, not all object fields are references. Julia considers some
.. types to be "plain data", meaning all of their data is self-contained
.. and does not reference other objects. The plain data types consist of bits
.. types (e.g. ``Int``) and immutable structs of other plain data types.
.. The initial contents of a plain data type is undefined::
这避免了持续检查 ``null`` 值。但是,所有对象的域都是引用。Julia 认为一些类型是“普通数据”,即他们的数据都是独立的,都不引用其他的对象。普通数据类型是由位类型或者其他普通数据类型的不可变数据结构所构成的(例如 ``Int`` )。普通数据类型的初始内容是未定义的: ::
julia> type HasPlain
n::Int
HasPlain() = new()
end
julia> HasPlain()
HasPlain(438103441441)
普通数据类型所构成的数组具有相同的行为。
.. Arrays of plain data types exhibit the same behavior.
可以在内部构造方法中,将不完整的对象传递给其它函数,来委托完成全部初始化: ::
type Lazy
xx
Lazy(v) = complete_me(new(), v)
end
如果 ``complete_me`` 或其它被调用的函数试图在初始化 ``Lazy`` 对象的 ``xx`` 域之前读取它,将会立即报错。
参数化构造方法
--------------
参数化构造方法的例子:
.. doctest::
julia> type Point{T<:Real}
x::T
y::T
end
## implicit T ##
julia> Point(1,2)
Point{Int64}(1,2)
julia> Point(1.0,2.5)
Point{Float64}(1.0,2.5)
julia> Point(1,2.5)
ERROR: `Point{T<:Real}` has no method matching Point{T<:Real}(::Int64, ::Float64)
## explicit T ##
julia> Point{Int64}(1,2)
Point{Int64}(1,2)
julia> Point{Int64}(1.0,2.5)
ERROR: InexactError()
julia> Point{Float64}(1.0,2.5)
Point{Float64}(1.0,2.5)
julia> Point{Float64}(1,2)
Point{Float64}(1.0,2.0)
上面的参数化构造方法等价于下面的声明: ::
type Point{T<:Real}
x::T
y::T
Point(x,y) = new(x,y)
end
Point{T<:Real}(x::T, y::T) = Point{T}(x,y)
内部构造方法只定义 ``Point{T}`` 的方法,而非 ``Point`` 的构造函数的方法。 ``Point`` 不是具体类型,不能有内部构造方法。外部构造方法定义了 ``Point`` 的构造方法。
可以将整数值 ``1`` “提升”为浮点数 ``1.0`` ,来完成构造:
.. doctest::
julia> Point(x::Int64, y::Float64) = Point(convert(Float64,x),y);
这样下例就可以正常运行:
.. doctest::
julia> Point(1,2.5)
Point{Float64}(1.0,2.5)
julia> typeof(ans)
Point{Float64} (constructor with 1 method)
但下例仍会报错:
.. doctest::
julia> Point(1.5,2)
ERROR: `Point{T<:Real}` has no method matching Point{T<:Real}(::Float64, ::Int64)
其实只需定义下列外部构造方法:
.. doctest::
julia> Point(x::Real, y::Real) = Point(promote(x,y)...);
``promote`` 函数将它的所有参数转换为相同类型。现在,所有的实数参数都可以正常运行:
.. doctest::
julia> Point(1.5,2)
Point{Float64}(1.5,2.0)
julia> Point(1,1//2)
Point{Rational{Int64}}(1//1,1//2)
julia> Point(1.0,1//2)
Point{Float64}(1.0,0.5)
案例:分数
----------
下面是 `rational.jl <https://github.com/JuliaLang/julia/blob/master/base/rational.jl>`_ 文件的开头部分,它实现了 Julia 的 :ref:`man-rational-numbers` : ::
immutable Rational{T<:Integer} <: Real
num::T
den::T
function Rational(num::T, den::T)
if num == 0 && den == 0
error("invalid rational: 0//0")
end
g = gcd(den, num)
num = div(num, g)
den = div(den, g)
new(num, den)
end
end
Rational{T<:Integer}(n::T, d::T) = Rational{T}(n,d)
Rational(n::Integer, d::Integer) = Rational(promote(n,d)...)
Rational(n::Integer) = Rational(n,one(n))
//(n::Integer, d::Integer) = Rational(n,d)
//(x::Rational, y::Integer) = x.num // (x.den*y)
//(x::Integer, y::Rational) = (x*y.den) // y.num
//(x::Complex, y::Real) = complex(real(x)//y, imag(x)//y)
//(x::Real, y::Complex) = x*y'//real(y*y')
function //(x::Complex, y::Complex)
xy = x*y'
yy = real(y*y')
complex(real(xy)//yy, imag(xy)//yy)
end
复数分数的例子:
.. doctest::
julia> (1 + 2im)//(1 - 2im)
-3//5 + 4//5*im
julia> typeof(ans)
Complex{Rational{Int64}} (constructor with 1 method)
julia> ans <: Complex{Rational}
false
================================================
FILE: manual/control-flow.rst
================================================
.. _man-control-flow:
********
控制流
********
Julia 提供一系列控制流:
- :ref:`man-compound-expressions` : ``begin`` 和 ``(;)``
- :ref:`man-conditional-evaluation` : ``if``-``elseif``-``else`` 和 ``?:`` (ternary operator)
- :ref:`man-short-circuit-evaluation` : ``&&``, ``||`` 和 chained comparisons
- :ref:`man-loops` : ``while`` 和 ``for``
- :ref:`man-exception-handling` : ``try``-``catch`` , ``error`` 和 ``throw``
- :ref:`man-tasks` : ``yieldto``
前五个控制流机制是高级编程语言的标准。但任务不是:它提供了非本地的控制流,便于在临时暂停的计算中进行切换。在 Julia 中,异常处理和协同多任务都是使用的这个机制。
.. _man-compound-expressions:
复合表达式
----------
用一个表达式按照顺序对一系列子表达式求值,并返回最后一个子表达式的值,有两种方法: ``begin`` 块和 ``(;)`` 链。 ``begin`` 块的例子:
.. doctest::
julia> z = begin
x = 1
y = 2
x + y
end
3
这个块很短也很简单,可以用 ``(;)`` 链语法将其放在一行上:
.. doctest::
julia> z = (x = 1; y = 2; x + y)
3
这个语法在 :ref:`man-functions` 中的单行函数定义非常有用。 ``begin`` 块也可以写成单行, ``(;)`` 链也可以写成多行:
.. doctest::
julia> begin x = 1; y = 2; x + y end
3
julia> (x = 1;
y = 2;
x + y)
3
.. _man-conditional-evaluation:
条件求值
--------
一个 ``if``-``elseif``-``else`` 条件表达式的例子: ::
if x < y
println("x is less than y")
elseif x > y
println("x is greater than y")
else
println("x is equal to y")
end
如果条件表达式 ``x < y`` 为真,相应的语句块将会被执行;否则就执行条件表达式 ``x > y`` ,如果结果为真, 相应的语句块将被执行;如果两个表达式都是假, ``else`` 语句块将被执行。这是它用在实际中的例子:
.. doctest::
julia> function test(x, y)
if x < y
println("x is less than y")
elseif x > y
println("x is greater than y")
else
println("x is equal to y")
end
end
test (generic function with 1 method)
julia> test(1, 2)
x is less than y
julia> test(2, 1)
x is greater than y
julia> test(1, 1)
x is equal to y
``elseif`` 及 ``else`` 块是可选的。
注意,很短 (单行) 的条件语句在 Julia 中经常被写为短路求值的形式 (见下节).
如果条件表达式的值是除 ``true`` 和 ``false`` 之外的值,会出错:
.. doctest::
julia> if 1
println("true")
end
ERROR: type: non-boolean (Int64) used in boolean context
“问号表达式”语法 ``?:`` 与 ``if``-``elseif``-``else`` 语法相关,但是适用于单个表达式: ::
a ? b : c
``?`` 之前的 ``a`` 是条件表达式,如果为 ``true`` ,就执行 ``:`` 之前的 ``b`` 表达式,如果为 ``false`` ,就执行 ``:`` 的 ``c`` 表达式。
用问号表达式来重写,可以使前面的例子更加紧凑。先看一个二选一的例子:
.. doctest::
julia> x = 1; y = 2;
julia> println(x < y ? "less than" : "not less than")
less than
julia> x = 1; y = 0;
julia> println(x < y ? "less than" : "not less than")
not less than
三选一的例子需要链式调用问号表达式:
.. doctest::
julia> test(x, y) = println(x < y ? "x is less than y" :
x > y ? "x is greater than y" : "x is equal to y")
test (generic function with 1 method)
julia> test(1, 2)
x is less than y
julia> test(2, 1)
x is greater than y
julia> test(1, 1)
x is equal to y
链式问号表达式的结合规则是从右到左。
与 ``if``-``elseif``-``else`` 类似, ``:`` 前后的表达式,只有在对应条件表达式为 ``true`` 或 ``false`` 时才执行:
.. doctest::
julia> v(x) = (println(x); x)
v (generic function with 1 method)
julia> 1 < 2 ? v("yes") : v("no")
yes
"yes"
julia> 1 > 2 ? v("yes") : v("no")
no
"no"
.. _man-short-circuit-evaluation:
短路求值
--------
``&&`` 和 ``||`` 布尔运算符被称为短路求值,它们连接一系列布尔表达式,仅计算最少的表达式来确定整个链的布尔值。这意味着:
- 在表达式 ``a && b`` 中,只有 ``a`` 为 ``true`` 时才计算子表达式 ``b``
- 在表达式 ``a || b`` 中,只有 ``a`` 为 ``false`` 时才计算子表达式 ``b``
``&&`` 和 ``||`` 都与右侧结合,但 ``&&`` 比 ``||`` 优先级高:
.. doctest::
julia> t(x) = (println(x); true)
t (generic function with 1 method)
julia> f(x) = (println(x); false)
f (generic function with 1 method)
julia> t(1) && t(2)
1
2
true
julia> t(1) && f(2)
1
2
false
julia> f(1) && t(2)
1
false
julia> f(1) && f(2)
1
false
julia> t(1) || t(2)
1
true
julia> t(1) || f(2)
1
true
julia> f(1) || t(2)
1
2
true
julia> f(1) || f(2)
1
2
false
这种方式在 Julia 里经常作为短 ``if`` 语句的一个简洁的替代。可以把 ``if <cond> <statement> end`` 写成
``<cond> && <statement>`` (读作 <cond> *从而* <statement>)。 类似地,可以把 ``if ! <cond> <statement> end``
写成 ``<cond> || <statement>`` (读作 <cond> *要不就* <statement>)。
例如, 递归阶乘可以这样写:
.. doctest::
julia> function factorial(n::Int)
n >= 0 || error("n must be non-negative")
n == 0 && return 1
n * factorial(n-1)
end
factorial (generic function with 1 method)
julia> factorial(5)
120
julia> factorial(0)
1
julia> factorial(-1)
ERROR: n must be non-negative
in factorial at none:2
*非* 短路求值运算符,可以使用 :ref:`man-mathematical-operations` 中介绍的位布尔运算符 ``&`` 和 ``|`` :
.. doctest::
julia> f(1) & t(2)
1
2
false
julia> t(1) | t(2)
1
2
true
``&&`` 和 ``||`` 的运算对象也必须是布尔值( ``true`` 或 ``false`` )。除了最后一项外,在短路求值中使用非布尔值是一个错误:
.. doctest::
julia> 1 && true
ERROR: type: non-boolean (Int64) used in boolean context
另一方面,短路求值的最后一项可以是任何类型的表达式。取决于之前的条件,它可以被求值并返回。
.. testsetup::
srand(123)
.. doctest::
julia> true && (x = rand(2,2))
2x2 Array{Float64,2}:
0.768448 0.673959
0.940515 0.395453
julia> false && (x = rand(2,2))
false
.. _man-loops:
重复求值: 循环
--------------
有两种循环表达式: ``while`` 循环和 ``for`` 循环。下面是 ``while`` 的例子:
.. doctest::
julia> i = 1;
julia> while i <= 5
println(i)
i += 1
end
1
2
3
4
5
上例也可以重写为 ``for`` 循环:
.. doctest::
julia> for i = 1:5
println(i)
end
1
2
3
4
5
此处的 ``1:5`` 是一个 ``Range`` 对象,表示的是 1, 2, 3, 4, 5 序列。 ``for`` 循环遍历这些数,将其逐一赋给变量 ``i`` 。 ``while`` 循环和 ``for`` 循环的另一区别是变量的作用域。如果在其它作用域中没有引入变量 ``i`` ,那么它仅存在于 ``for`` 循环中。不难验证:
.. doctest::
julia> for j = 1:5
println(j)
end
1
2
3
4
5
julia> j
ERROR: j not defined
有关变量作用域,详见 :ref:`man-variables-and-scoping` 。
通常, ``for`` 循环可以遍历任意容器。这时,应使用另一个(但是完全等价的)关键词 ``in`` ,而不是 ``=`` ,它使得代码更易阅读:
.. doctest::
julia> for i in [1,4,0]
println(i)
end
1
4
0
julia> for s in ["foo","bar","baz"]
println(s)
end
foo
bar
baz
手册中将介绍各种可迭代容器(详见 :ref:`man-arrays` )。
有时要提前终止 ``while`` 或 ``for`` 循环。可以通过关键词 ``break`` 来实现:
.. doctest::
julia> i = 1;
julia> while true
println(i)
if i >= 5
break
end
i += 1
end
1
2
3
4
5
julia> for i = 1:1000
println(i)
if i >= 5
break
end
end
1
2
3
4
5
有时需要中断本次循环,进行下一次循环,这时可以用关键字 ``continue`` :
.. doctest::
julia> for i = 1:10
if i % 3 != 0
continue
end
println(i)
end
3
6
9
多层 ``for`` 循环可以被重写为一个外层循环,迭代类似于笛卡尔乘积的形式:
.. doctest::
julia> for i = 1:2, j = 3:4
println((i, j))
end
(1,3)
(1,4)
(2,3)
(2,4)
这种情况下用 ``break`` 会直接跳出所有循环,而不仅仅是最内层的循环。
.. _man-exception-handling:
异常处理
--------
当遇到意外条件时,函数可能无法给调用者返回一个合理值。这时,要么终止程序,打印诊断错误信息;要么程序员编写异常处理。
内置异常 ``Exception``
~~~~~~~~~~~~~~~~~~~~~~
如果程序遇到意外条件,异常将会被抛出。表中列出内置异常。
+------------------------+
| ``Exception`` |
+========================+
| ``ArgumentError`` |
+------------------------+
| ``BoundsError`` |
+------------------------+
| ``DivideError`` |
+------------------------+
| ``DomainError`` |
+------------------------+
| ``EOFError`` |
+------------------------+
| ``ErrorException`` |
+------------------------+
| ``InexactError`` |
+------------------------+
| ``InterruptException`` |
+------------------------+
| ``KeyError`` |
+------------------------+
| ``LoadError`` |
+------------------------+
| ``MemoryError`` |
+------------------------+
| ``MethodError`` |
+------------------------+
| ``OverflowError`` |
+------------------------+
| ``ParseError`` |
+------------------------+
| ``SystemError`` |
+------------------------+
| ``TypeError`` |
+------------------------+
| ``UndefRefError`` |
+------------------------+
| ``UndefVarError`` |
+------------------------+
例如,当对负实数使用内置的 ``sqrt`` 函数时,将抛出 ``DomainError()`` :
.. doctest::
julia> sqrt(-1)
ERROR: DomainError
sqrt will only return a complex result if called with a complex argument.
try sqrt(complex(x))
in sqrt at math.jl:131
你可以用以下方式定义自己的异常:
.. doctest::
julia> type MyCustomException <: Exception end
``throw`` 函数
~~~~~~~~~~~~~~
可以使用 ``throw`` 函数显式创建异常。例如,某个函数只对非负数做了定义,如果参数为负数,可以抛出 ``DomaineError`` 异常:
.. doctest::
julia> f(x) = x>=0 ? exp(-x) : throw(DomainError())
f (generic function with 1 method)
julia> f(1)
0.36787944117144233
julia> f(-1)
ERROR: DomainError
in f at none:1
注意, ``DomainError`` 使用时需要使用带括号的形式,否则返回的并不是异常,而是异常的类型。必须带括号才能返回 ``Exception`` 对象:
.. doctest::
julia> typeof(DomainError()) <: Exception
true
julia> typeof(DomainError) <: Exception
false
另外,某些异常接受一个或多个参数:
.. doctest::
julia> throw(UndefVarError(:x))
ERROR: x not defined
仿照 ``UndefVarError`` 定义的方式,这个机制也可以在自定义异常类型中使用:
.. doctest::
julia> type MyUndefVarError <: Exception
var::Symbol
end
julia> Base.showerror(io::IO, e::MyUndefVarError) = print(io, e.var, " not defined");
``error`` 函数
~~~~~~~~~~~~~~
``error`` 函数用来产生 ``ErrorException`` ,阻断程序的正常执行。
如下改写 ``sqrt`` 函数,当参数为负数时,提示错误,立即停止执行:
.. doctest::
julia> fussy_sqrt(x) = x >= 0 ? sqrt(x) : error("negative x not allowed")
fussy_sqrt (generic function with 1 method)
julia> fussy_sqrt(2)
1.4142135623730951
julia> fussy_sqrt(-1)
ERROR: negative x not allowed
in fussy_sqrt at none:1
当对负数调用 ``fussy_sqrt`` 时,它会立即返回,显示错误信息:
.. doctest::
julia> function verbose_fussy_sqrt(x)
println("before fussy_sqrt")
r = fussy_sqrt(x)
println("after fussy_sqrt")
return r
end
verbose_fussy_sqrt (generic function with 1 method)
julia> verbose_fussy_sqrt(2)
before fussy_sqrt
after fussy_sqrt
1.4142135623730951
julia> verbose_fussy_sqrt(-1)
before fussy_sqrt
ERROR: negative x not allowed
in verbose_fussy_sqrt at none:3
``warn`` 和 ``info`` 函数
~~~~~~~~~~~~~~~~~~~~~~~~~
Julia 还提供一些函数,用来向标准错误 I/O 输出一些消息,但不抛出异常,因而并不会打断程序的执行:
.. doctest::
julia> info("Hi"); 1+1
INFO: Hi
2
julia> warn("Hi"); 1+1
WARNING: Hi
2
julia> error("Hi"); 1+1
ERROR: Hi
in error at error.jl:21
``try/catch`` 语句
~~~~~~~~~~~~~~~~~~
``try/catch`` 语句可以用于处理一部分预料中的异常 ``Exception`` 。例如,下面求平方根函数可以正确处理实数或者复数:
.. doctest::
julia> f(x) = try
sqrt(x)
catch
sqrt(complex(x, 0))
end
f (generic function with 1 method)
julia> f(1)
1.0
julia> f(-1)
0.0 + 1.0im
但是处理异常比正常采用分支来处理,会慢得多。
``try/catch`` 语句使用时也可以把异常赋值给某个变量。例如:
.. doctest::
julia> sqrt_second(x) = try
sqrt(x[2])
catch y
if isa(y, DomainError)
sqrt(complex(x[2], 0))
elseif isa(y, BoundsError)
sqrt(x)
end
end
sqrt_second (generic function with 1 method)
julia> sqrt_second([1 4])
2.0
julia> sqrt_second([1 -4])
0.0 + 2.0im
julia> sqrt_second(9)
3.0
julia> sqrt_second(-9)
ERROR: DomainError
in sqrt_second at none:7
注意,紧跟 ``catch`` 的符号会作为异常的名字,所以在将 ``try/catch`` 写在单行内的时候需要特别注意。下面的代码 *不会* 在发生错误的时候返回 ``x`` 的值::
try bad() catch x end
相对的,使用分号或在 ``catch`` 后另起一行::
try bad() catch; x end
try bad()
catch
x
end
Julia 还提供了更高级的异常处理函数 ``rethrow`` , ``backtrace`` 和 ``catch_backtrace`` 。
finally 语句
~~~~~~~~~~~~
在改变状态或者使用文件等资源时,通常需要在操作执行完成时做清理工作(比如关闭文件)。异常的存在使得这样的任务变得复杂,因为异常会导致程序提前退出。关键字 ``finally`` 可以解决这样的问题,无论程序是怎样退出的, ``finally`` 语句总是会被执行。
例如, 下面的程序说明了怎样保证打开的文件总是会被关闭: ::
f = open("file")
try
# operate on file f
finally
close(f)
end
当程序执行完 ``try`` 语句块(例如因为执行到 ``return`` 语句,或者只是正常完成), ``close`` 语句将会被执行。如果 ``try`` 语句块因为异常提前退出,异常将会继续传播。 ``catch`` 语句可以和 ``try`` , ``finally`` 一起使用。这时。 ``finally`` 语句将会在 ``catch`` 处理完异常之后执行。
.. _man-tasks:
任务(也称为协程)
------------------
任务是一种允许计算灵活地挂起和恢复的控制流,有时也被称为对称协程、轻量级线程、协同多任务等。
如果一个计算(比如运行一个函数)被设计为 ``Task`` ,有可能因为切换到其它 ``Task`` 而被中断。原先的 ``Task`` 在以后恢复时,会从原先中断的地方继续工作。切换任务不需要任何空间,同时可以有任意数量的任务切换,而不需要考虑堆栈问题。任务切换与函数调用不同,可以按照任何顺序来进行。
任务比较适合生产者-消费者模式,一个过程用来生产值,另一个用来消费值。消费者不能简单的调用生产者来得到值,因为两者的执行时间不一定协同。在任务中,两者则可以
正常运行。
Julia 提供了 ``produce`` 和 ``consume`` 函数来解决这个问题。生产者调用 ``produce`` 函数来生产值:
.. doctest::
julia> function producer()
produce("start")
for n=1:4
produce(2n)
end
produce("stop")
end;
要消费生产的值,先对生产者调用 ``Task`` 函数,然后对返回的对象重复调用 ``consume`` :
.. doctest::
julia> p = Task(producer);
julia> consume(p)
"start"
julia> consume(p)
2
julia> consume(p)
4
julia> consume(p)
6
julia> consume(p)
8
julia> consume(p)
"stop"
可以在 ``for`` 循环中迭代任务,生产的值被赋值给循环变量:
.. doctest::
julia> for x in Task(producer)
println(x)
end
start
2
4
6
8
stop
注意 ``Task()`` 函数的参数,应为零参函数。生产者常常是参数化的,因此需要为其构造零参 :ref:`匿名函数 <man-anonymous-functions>` 。可以直接写,也可以调用宏: ::
function mytask(myarg)
...
end
taskHdl = Task(() -> mytask(7))
# 也可以写成
taskHdl = @task mytask(7)
``produce`` 和 ``consume`` 但它并不在不同的 CPU 发起线程。我们将在 :ref:`man-parallel-computing` 中,讨论真正的内核线程。
核心任务操作
~~~~~~~~~~~~~~~~~~~~
尽管 ``produce`` 和 ``consume`` 已经阐释了任务的本质,他们实际上是由库函数调用更原始的函数
``yieldto`` 实现的。 ``yieldto(task,value)`` 挂起当前任务,切换到指定的 ``task`` ,
并使这个 ``task`` 的上一次 ``yeidlto`` 返回指定的 ``value`` 。注意 ``yieldto``
是任务风格的控制流唯一需要的操作;取代调用和返回,我们只用在不同的任务之间切换即可。这就是为什么
这个特性被称做 “对称式协程”:每一个任务的切换都是用相同的机制。
尽管 ``yeildto`` 很强大,但是大多数任务并不直接调用它。这当中的原因可以理解。当你从当前的任务
切换走,你一般还会切换回来。然而正确的处理切换的时机和任务需要相当的协调。例如, ``procude`` 需要保持某个状态来记录消费者。无需手动地记录正在消费的任务让 ``produce`` 比 ``yieldto`` 更容易使用。
除 ``yieldto`` 之外,我们也需要一些其他的基本函数以高效地使用任务。
``current_task()`` 获得当前运行任务的引用。
``istaskdone(t)`` 查询任务是否终止。
``istaskstarted(t)`` 查询任务是否启动。
``task_local_storage`` 处理当前任务的键值储存。
任务与事件
~~~~~~~~~~~~~~~~
大多数任务的切换发生在等待 I/O 请求这样的事件的时候,并由标准库的调度器完成。调度器记录正在运行
的任务的队列,并执行一个循环来根据外部事件(比如消息到达)重启任务。
处理等待事件的基本函数是 ``wait`` 。有几种对象实现了 ``wait``,比如对于 ``Process`` 对象,
``wait`` 会等待它终止。更多的时候 ``wait`` 是隐式的, 比如 ``wait`` 可以发生在调用
``read`` 的时候,以等待可用数据。
在所有的情况中, ``wait`` 最终会操作在一个负责将任务排队和重启的 ``Condition`` 对象上。
当任务在 ``Condition`` 上调用 ``wait`` ,任务会被标记为不可运行,并被加入到 ``Condition``
的队列中,再切换至调度器。调度器会选取另一个任务来运行,或者等待外部事件。如果一切正常,最终一个
事件句柄会在 ``Condition`` 上调用 ``notify`` ,使正在等待的任务再次变得可以运行。
调用 ``Task`` 可以生成一个未被调度器管理的任务,这允许你用 ``yieldto`` 手动管理任务。不管怎样,
当这样的任务正在等待事件时,事件一旦发生,它仍然会自动重启。而且任何时候你都可以调用
``schedule(task)`` 或者用宏 ``@schedule`` 或 ``@async`` 来让调度器来运行一个任务,
而不用去等待任何事件。(参见 :ref:`man-parallel-computing`)
任务状态
~~~~~~~~
任务包含一个 ``state`` 域,它用来描述任务的执行状态。任务状态取如下的几种符号中的一种:
============= ==================================================
符号 意义
============= ==================================================
``:runnable`` 任务正在运行,或可被切换到该任务
``:waiting`` 正在阻塞等待事件
``:queued`` 在调度器的队列中将要重新开始运行
``:done`` 成功执行完毕
``:failed`` 由于未处理的异常而终止
============= ==================================================
================================================
FILE: manual/conversion-and-promotion.rst
================================================
.. _man-conversion-and-promotion:
********************
类型转换和类型提升
********************
Julia 可以将数学运算符的参数提升为同一个类型,这些参数的类型曾经在 :ref:`man-integers-and-floating-point-numbers` , :ref:`man-mathematical-operations` , :ref:`man-types` ,及 :ref:`man-methods` 中提到过。
在某种意义上,Julia 是“非自动类型提升”的:数学运算符只是有特殊语法的函数,函数的参数不会被自动转换。但通过重载,仍能做到“自动”类型提升。
.. _man-conversion:
类型转换
--------
``convert`` 函数用于将值转换为各种类型。它有两个参数:第一个是类型对象,第二个是要转换的值;返回值是转换为指定类型的值:
.. doctest::
julia> x = 12
12
julia> typeof(x)
Int64
julia> convert(Uint8, x)
0x0c
julia> typeof(ans)
Uint8
julia> convert(FloatingPoint, x)
12.0
julia> typeof(ans)
Float64
遇到不能转换时, ``convert`` 会引发 “no method” 错误:
.. doctest::
julia> convert(FloatingPoint, "foo")
ERROR: `convert` has no method matching convert(::Type{FloatingPoint}, ::ASCIIString)
in convert at base.jl:13
Julia 不做字符串和数字之间的类型转换。
定义新类型转换
~~~~~~~~~~~~~~
要定义新类型转换,只需给 ``convert`` 提供新方法即可。下例将数值转换为布尔值: ::
convert(::Type{Bool}, x::Number) = (x!=0)
此方法第一个参数的类型是 :ref:`单态类型 <man-singleton-types>` , ``Bool`` 是 ``Type{Bool}`` 的唯一实例。此方法仅在第一个参数是 ``Bool`` 才调用。
Notice the syntax used for the first
argument: the argument name is omitted prior to the ``::`` symbol, and only
the type is given. This is the syntax in Julia for a function argument whose type is
specified but whose value is never used in the function body. In this example,
since the type is a singleton, there would never be any reason to use its value
within the body.
转换时检查数值是否为 0 :
.. doctest::
julia> convert(Bool, 1)
true
julia> convert(Bool, 0)
false
julia> convert(Bool, 1im)
ERROR: InexactError()
in convert at complex.jl:18
julia> convert(Bool, 0im)
false
实际使用的类型转换都比较复杂,下例是 Julia 中的一个实现: ::
convert{T<:Real}(::Type{T}, z::Complex) = (imag(z)==0 ? convert(T,real(z)) :
throw(InexactError()))
julia> convert(Bool, 1im)
InexactError()
in convert at complex.jl:40
案例:分数类型转换
~~~~~~~~~~~~~~~~~~
继续 Julia 的 ``Rational`` 类型的案例研究, `rational.jl <https://github.com/JuliaLang/julia/blob/master/base/rational.jl>`_ 中类型转换的声明紧跟在类型声明和构造函数之后: ::
convert{T<:Integer}(::Type{Rational{T}}, x::Rational) = Rational(convert(T,x.num),convert(T,x.den))
convert{T<:Integer}(::Type{Rational{T}}, x::Integer) = Rational(convert(T,x), convert(T,1))
function convert{T<:Integer}(::Type{Rational{T}}, x::FloatingPoint, tol::Real)
if isnan(x); return zero(T)//zero(T); end
if isinf(x); return sign(x)//zero(T); end
y = x
a = d = one(T)
b = c = zero(T)
while true
f = convert(T,round(y)); y -= f
a, b, c, d = f*a+c, f*b+d, a, b
if y == 0 || abs(a/b-x) <= tol
return a//b
end
y = 1/y
end
end
convert{T<:Integer}(rt::Type{Rational{T}}, x::FloatingPoint) = convert(rt,x,eps(x))
convert{T<:FloatingPoint}(::Type{T}, x::Rational) = convert(T,x.num)/convert(T,x.den)
convert{T<:Integer}(::Type{T}, x::Rational) = div(convert(T,x.num),convert(T,x.den))
前四个定义可确保 ``a//b == convert(Rational{Int64}, a/b)`` 。后两个把分数转换为浮点数和整数类型。
.. _man-promotion:
类型提升
--------
类型提升是指将各种类型的值转换为同一类型。它与类型等级关系无关,例如,每个 ``Int32`` 值都可以被表示为 ``Float64`` 值,但 ``Int32`` 不是 ``Float64`` 的子类型。
Julia 使用 ``promote`` 函数来做类型提升,其参数个数可以是任意多,它返回同样个数的同一类型的多元组;如果不能提升,则抛出异常。类型提升常用来将数值参数转换为同一类型:
.. doctest::
julia> promote(1, 2.5)
(1.0,2.5)
julia> promote(1, 2.5, 3)
(1.0,2.5,3.0)
julia> promote(2, 3//4)
(2//1,3//4)
julia> promote(1, 2.5, 3, 3//4)
(1.0,2.5,3.0,0.75)
julia> promote(1.5, im)
(1.5 + 0.0im,0.0 + 1.0im)
julia> promote(1 + 2im, 3//4)
(1//1 + 2//1*im,3//4 + 0//1*im)
浮点数值提升为最高的浮点数类型。整数值提升为本地机器的原生字长或最高的整数值类型。既有整数也有浮点数时,提升为可以包括所有值的浮点数类型。既有整数也有分数时,提升为分数。既有分数也有浮点数时,提升为浮点数。既有复数也有实数时,提升为适当的复数。
数值运算中,数学运算符 ``+``, ``-``, ``*`` 和 ``/`` 等方法定义,都“巧妙”的应用了类型提升。下例是 `promotion.jl <https://github.com/JuliaLang/julia/blob/master/base/promotion.jl>`_ 中的一些定义: ::
+(x::Number, y::Number) = +(promote(x,y)...)
-(x::Number, y::Number) = -(promote(x,y)...)
*(x::Number, y::Number) = *(promote(x,y)...)
/(x::Number, y::Number) = /(promote(x,y)...)
`promotion.jl <https://github.com/JuliaLang/julia/blob/master/base/promotion.jl>`_ 中还定义了其它算术和数学运算类型提升的方法,但 Julia 标准库中几乎没有调用 ``promote`` 。 ``promote`` 一般用在外部构造方法中,便于使构造函数适应各种不同类型的参数。 `rational.jl <https://github.com/JuliaLang/julia/blob/master/base/rational.jl>`_ 中提供了如下的外部构造方法: ::
Rational(n::Integer, d::Integer) = Rational(promote(n,d)...)
此方法的例子:
.. doctest::
julia> Rational(int8(15),int32(-5))
-3//1
julia> typeof(ans)
Rational{Int64} (constructor with 1 method)
对自定义类型来说,最好由程序员给构造函数显式提供所期待的类型。但处理数值问题时,做自动类型提升比较方便。
定义类型提升规则
~~~~~~~~~~~~~~~~
尽管可以直接给 ``promote`` 函数定义方法,但这太麻烦了。我们用辅助函数 ``promote_rule`` 来定义 ``promote`` 的行为。 ``promote_rule`` 函数接收类型对象对儿,返回另一个类型对象。此函数将参数中的类型的实例,提升为要返回的类型: ::
promote_rule(::Type{Float64}, ::Type{Float32} ) = Float64
提升后的类型不需要与函数的参数类型相同。下面是 Julia 标准库中的例子: ::
promote_rule(::Type{Uint8}, ::Type{Int8}) = Int
promote_rule(::Type{Char}, ::Type{Uint8}) = Int32
不需要同时定义 ``promote_rule(::Type{A}, ::Type{B})`` 和 ``promote_rule(::Type{B}, ::Type{A})`` —— ``promote_rule`` 函数在提升过程中隐含了对称性。
``promote_type`` 函数使用 ``promote_rule`` 函数来定义,它接收任意个数的类型对象,返回它们作为 ``promote`` 参数时,所应返回值的公共类型。因此可以使用 ``promote_type`` 来了解特定类型的组合会提升为哪种类型:
.. doctest::
julia> promote_type(Int8, Uint16)
Int64
``promote`` 使用 ``promote_type`` 来决定类型提升时要把参数值转换为哪种类型。完整的类型提升机制可见 `promotion.jl <https://github.com/JuliaLang/julia/blob/master/base/promotion.jl>`_ ,一共有 35 行。
案例:分数类型提升
~~~~~~~~~~~~~~~~~~
我们结束 Julia 分数类型的案例: ::
promote_rule{T<:Integer}(::Type{Rational{T}}, ::Type{T}) = Rational{T}
promote_rule{T<:Integer,S<:Integer}(::Type{Rational{T}}, ::Type{S}) = Rational{promote_type(T,S)}
promote_rule{T<:Integer,S<:Integer}(::Type{Rational{T}}, ::Type{Rational{S}}) = Rational{promote_type(T,S)}
promote_rule{T<:Integer,S<:FloatingPoint}(::Type{Rational{T}}, ::Type{S}) = promote_type(T,S)
================================================
FILE: manual/dates.rst
================================================
.. _man-dates:
*********
日期和时间
*********
``Dates`` 模块提供了两种关于时间的数据类型: ``Date`` 和 ``DateTime``, 精度分别为天和毫秒, 都是抽象数据类型 ``TimeType`` 的子类型. 使用两种数据类型的原因很简单: 某些操作本身很简单, 无论是从代码上看还是逻辑上, 使用高精度的数据类型是完全没有必要的. 例如, ``Date`` 只精确到天 (也就是说, 没有小时, 分钟或者秒), 所以使用时就不需要考虑时区, 夏令时和闰秒.
``Date`` 和 ``DateTime`` 都不过是 ``Int64`` 的简单封装, 仅有的一个成员变量 ``instant`` 实际上的类型是 ``UTInstant{P}``, 代表的是基于世界时的机器时间 [1]_. ``Datetime`` 类型是 *不考虑时区* 的 (根据 Python 的讲法), 或者说是 Java 8 里面的 *本地时间*. 额外的时间日期操作可以通过 `Timezones.jl 扩展包 <https://github.com/quinnj/Timezones.jl/>`_ 来获取, 其中的数据来自 `Olsen Time Zone Database <http://www.iana.org/time-zones>`_. ``Date`` 和 ``DateTime`` 遵循 ISO 8601 标准. 值得注意的一点是, ISO 8601 关于公元前日期的处理比较特殊. 简单来说, 公元前的最后一天是公元前 1-12-31, 接下来第二天是公元 1-1-1, 所以是没有公元 0 年存在的. 而 ISO 标准认定, 公元前 1 年是 0 年, 所以 ``0000-12-21`` 是 ``0001-01-01`` 的前一天, ``-0001`` 是公元前 2 年, ``-0003`` 是公元前 3 年, 等等.
.. [1] 一般来说有两种常用的时间表示法, 一种是基于地球的自转状态 (地球转一整圈 = 1 天), 另一种基于 SI 秒 (固定的常量). 这两种表示方法是不一样的. 试想一下, 因为地球自转, 基于世界时的的秒可能是不等长的. 但总得来说, 基于世界时的 ``Date`` 和 ``DateTime`` 是一种简化的方案, 例如闰秒的情况不需要考虑. 这种表示时间的方案的正式名称为 `世界时 <http://en.wikipedia.org/wiki/Universal_Time>`_. 这意味着, 每一分钟有 60 秒, 每一天有 60 小时, 这样使得关于时间的计算更自然, 简单.
构造函数
-------
``Date`` 和 ``DateType`` 可以通过整数或者 ``Period`` 构造, 通过直接传入, 或者作为与特定时间的差值::
julia> DateTime(2013)
2013-01-01T00:00:00
julia> DateTime(2013,7)
2013-07-01T00:00:00
julia> DateTime(2013,7,1)
2013-07-01T00:00:00
julia> DateTime(2013,7,1,12)
2013-07-01T12:00:00
julia> DateTime(2013,7,1,12,30)
2013-07-01T12:30:00
julia> DateTime(2013,7,1,12,30,59)
2013-07-01T12:30:59
julia> DateTime(2013,7,1,12,30,59,1)
2013-07-01T12:30:59.001
julia> Date(2013)
2013-01-01
julia> Date(2013,7)
2013-07-01
julia> Date(2013,7,1)
2013-07-01
julia> Date(Dates.Year(2013),Dates.Month(7),Dates.Day(1))
2013-07-01
julia> Date(Dates.Month(7),Dates.Year(2013))
2013-07-01
``Date`` 和 ``DateTime`` 解析是通过格式化的字符串实现的. 格式化的字符串是指 *分隔* 的或者 *固定宽度* 的 "字符段" 来表示一段时间, 然后传递给 ``Date`` 或者 ``DateTime`` 的构造函数.
使用分隔的字符段方法, 需要显示指明分隔符, 所以 ``"y-m-d"`` 告诉解析器第一个和第二个字符段中间有一个 ``-``, 例如 ``"2014-07-16"``, ``y``, ``m`` 和 ``d`` 字符告诉解析器每个字符段的含义.
固定宽度字符段是使用固定宽度的字符串来表示时间. 所以 ``"yyyymmdd"`` 相对应的时间字符串为 ``"20140716"``.
同时字符表示的月份也可以被解析, 通过使用 ``u`` 和 ``U``, 分别是月份的简称和全称. 默认支持英文的月份名称, 所以 ``u`` 对应于 ``Jan``, ``Feb``, ``Mar`` 等等, ``U`` 对应于 ``January``, ``February``, ``March`` 等等. 然而, 同 ``dayname`` 和 ``monthname`` 一样, 本地化的输出也可以实现, 通过向 ``Dates.MONTHTOVALUEABBR`` 和 ``Dates.MONTHTOVALUE`` 字典添加 ``locale=>Dict{UTF8String, Int}`` 类型的映射.
更多的解析和格式化的例子可以参考 `tests/dates/io.jl <https://github.com/JuliaLang/julia/blob/master/test/dates/io.jl>`_.
时间间隔/比较
----------
计算两个 ``Date`` 或者 ``DateTime`` 之间的间隔是很直观的, 考虑到他们不过是 ``UTInstant{Day}`` 和 ``UTInstant{Millisecond}`` 的简单封装. 不同点是, 计算两个 ``Date`` 的时间间隔, 返回的是 ``Day``, 而计算 ``DateTime`` 时间间隔返回的是 ``Millisecond``. 同样的, 比较两个 ``TimeType`` 本质上是比较两个 ``Int64``
::
julia> dt = Date(2012,2,29)
2012-02-29
julia> dt2 = Date(2000,2,1)
2000-02-01
julia> dump(dt)
Date
instant: UTInstant{Day}
periods: Day
value: Int64 734562
julia> dump(dt2)
Date
instant: UTInstant{Day}
periods: Day
value: Int64 730151
julia> dt > dt2
true
julia> dt != dt2
true
julia> dt + dt2
Operation not defined for TimeTypes
julia> dt * dt2
Operation not defined for TimeTypes
julia> dt / dt2
Operation not defined for TimeTypes
julia> dt - dt2
4411 days
julia> dt2 - dt
-4411 days
julia> dt = DateTime(2012,2,29)
2012-02-29T00:00:00
julia> dt2 = DateTime(2000,2,1)
2000-02-01T00:00:00
julia> dt - dt2
381110402000 milliseconds
访问函数
-------
因为 ``Date`` 和 ``DateTime`` 类型是使用 ``Int64`` 的封装, 具体的某一部分可以通过访问函数来获得. 小写字母的获取函数返回值为整数 ::
julia> t = Date(2014,1,31)
2014-01-31
julia> Dates.year(t)
2014
julia> Dates.month(t)
1
julia> Dates.week(t)
5
julia> Dates.day(t)
31
大写字母的获取函数返回值为 ``Period`` ::
julia> Dates.Year(t)
2014 years
julia> Dates.Day(t)
31 days
如果需要一次性获取多个字段, 可以使用符合函数 ::
julia> Dates.yearmonth(t)
(2014,1)
julia> Dates.monthday(t)
(1,31)
julia> Dates.yearmonthday(t)
(2014,1,31)
也可以直接获取底层的 ``UTInstant`` 或 整数数值 ::
julia> dump(t)
Date
instant: UTInstant{Day}
periods: Day
value: Int64 735264
julia> t.instant
UTInstant{Day}(735264 days)
julia> Dates.value(t)
735264
查询函数
-------
查询函数可以用来获得关于 ``TimeType`` 的额外信息, 例如某个日期是星期几 ::
julia> t = Date(2014,1,31)
2014-01-31
julia> Dates.dayofweek(t)
5
julia> Dates.dayname(t)
"Friday"
julia> Dates.dayofweekofmonth(t)
5 # 5th Friday of January
月份信息 ::
julia> Dates.monthname(t)
"January"
julia> Dates.daysinmonth(t)
31
年份信息和季节信息 ::
julia> Dates.isleapyear(t)
false
julia> Dates.dayofyear(t)
31
julia> Dates.quarterofyear(t)
1
julia> Dates.dayofquarter(t)
31
``dayname`` 和 ``monthname`` 可以传入可选参数 ``locale`` 来显示本地化的日期显示 ::
julia> const french_daysofweek =
[1=>"Lundi",2=>"Mardi",3=>"Mercredi",4=>"Jeudi",5=>"Vendredi",6=>"Samedi",7=>"Dimanche"];
# Load the mapping into the Dates module under locale name "french"
julia> Dates.VALUETODAYOFWEEK["french"] = french_daysofweek;
julia> Dates.dayname(t;locale="french")
"Vendredi"
``monthname`` 与之类似的, 这时, ``Dates.VALUETOMONTH`` 需要加载 ``locale=>Dict{Int, UTF8String}``.
时间间隔算术运算
------------
在使用任何一门编程语言/时间日期框架前, 最好了解下时间间隔是怎么处理的, 因为有些地方需要 `特殊的技巧 <http://msmvps.com/blogs/jon_skeet/archive/2010/12/01/the-joys-of-date-time-arithmetic.aspx>`_.
``Dates`` 模块的工作方式是这样的, 在做 ``period`` 算术运算时, 每次都做尽量小的改动. 这种方式被称之为 *日历* 算术, 或者就是平时日常交流中惯用的方式. 这些到底是什么? 举个经典的例子: 2014年1月31号加一月. 答案是什么? JavaScript 会得出 `3月3号 <http://www.markhneedham.com/blog/2009/01/07/javascript-add-a-month-to-a-date/>`_ (假设31天). PHP 会得到 `3月2号 <http://stackoverflow.com/questions/5760262/php-adding-months-to-a-date-while-not-exceeding-the-last-day-of-the-month>`_ (假设30天). 事实上, 这个问题没有正确答案. ``Dates`` 模块会给出 2月28号的答案. 它是怎么得出的? 试想下赌场的 7-7-7 赌博游戏.
设想下, 赌博机的槽不是 7-7-7, 而是年-月-日, 或者在我们的例子中, 2014-01-31. 当你想要在这个日期上增加一个月时, 对应于月份的那个槽会增加1, 所以现在是 2014-02-31, 然后检查年-月-日中的日是否超过了这个月最大的合法的数字 (28). 这种方法有什么后果呢? 我们继续加上一个月, ``2014-02-28 + Month(1) == 2014-03-28``. 什么? 你是不是期望结果是3月的最后一天? 抱歉, 不是的, 想一下 7-7-7. 因为要改变尽量少的槽, 所以我们在月份上加1, 2014-03-28, 然后就没有然后了, 因为这是个合法的日期. 然而, 如果我们在原来的日期(2014-01-31)上加上2个月, 我们会得到预想中的 2014-03-31. 这种方式带来的另一个问题是损失了可交换性, 如果强制加法的顺序的话 (也就是说,用不用的顺序相加会得到不同的结果). 例如 ::
julia> (Date(2014,1,29)+Dates.Day(1)) + Dates.Month(1)
2014-02-28
julia> (Date(2014,1,29)+Dates.Month(1)) + Dates.Day(1)
2014-03-01
这是怎么回事? 第一个例子中, 我们往1月29号加上一天, 得到 2014-01-30; 然后加上一月, 得到 2014-02-30, 然后被调整到 2014-02-28. 在第二个例子中, 我们 *先* 加一个月, 得到 2014-02-29, 然后被调整到 2014-02-28, *然后* 加一天, 得到 2014-03-01. 在处理这种问题时的一个设计原则是, 如果有多个时间间隔, 操作的顺序是按照间隔的 *类型* 排列的, 而不是按照他们的值大小或者出现顺序; 这就是说, 第一个加的是 ``Year``, 然后是 ``Month``, 然后是 ``Week``, 等等. 所以下面的例子 *是* 符合可交换性的 ::
julia> Date(2014,1,29) + Dates.Day(1) + Dates.Month(1)
2014-03-01
julia> Date(2014,1,29) + Dates.Month(1) + Dates.Day(1)
2014-03-01
很麻烦? 也许吧. 一个 ``Dates`` 的初级用户该怎么办呢? 最基本的是要清楚, 当操作月份时, 如果强制指明操作的顺序, 可能会产生意想不到的结果, 其他的就没什么了. 幸运的是, 这基本就是所有的特殊情况了 (UT 时间已经免除了夏令时, 闰秒之类的麻烦).
调整函数
-------
时间间隔的算术运算是很方便, 但同时, 有些时间的操作是基于 *日历* 或者 *时间* 本身的, 而不是一个固定的时间间隔. 例如假期的计算, 诸如 "纪念日 = 五月的最后一个周一", 或者 "感恩节 = 十一月的第四个周四". 这些时间的计算牵涉到基于日历的规则, 例如某个月的第一天或者最后一天, 下一个周四, 或者第一个和第三个周三, 等等.
``Dates`` 模块提供几个了 *调整* 函数, 这样可以简单简洁的描述时间规则. 第一组是关于周, 月, 季度, 年的第一和最后一个元素. 函数参数为 ``TimeType``, 然后按照规则返回或者 *调整* 到正确的日期.
::
# 调整时间到相应的周一
julia> Dates.firstdayofweek(Date(2014,7,16))
2014-07-14
# 调整时间到这个月的最后一天
julia> Dates.lastdayofmonth(Date(2014,7,16))
2014-07-31
# 调整时间到这个季度的最后一天
julia> Dates.lastdayofquarter(Date(2014,7,16))
2014-09-30
接下来一组高阶函数, ``tofirst``, ``tolast``, ``tonext``, and ``toprev``, 第一个参数为 ``DateFunction``, 第二个参数 ``TimeType`` 作为起点日期. 一个 ``DateFunction`` 类型的变量是一个函数, 通常是匿名函数, 这个函数接受 ``TimeType`` 作为输入, 返回 ``Bool``, ``true`` 来表示是否满足特定的条件. 例如 ::
julia> istuesday = x->Dates.dayofweek(x) == Dates.Tuesday # 如果是周二, 返回 true
(anonymous function)
julia> Dates.tonext(istuesday, Date(2014,7,13)) # 2014-07-13 is a 是周日
2014-07-15
# 同时也额外提供了一些函数, 使得对星期几之类的操作更加方便
julia> Dates.tonext(Date(2014,7,13), Dates.Tuesday)
2014-07-15
如果是复杂的时间表达式, 使用 do-block 会很方便 ::
julia> Dates.tonext(Date(2014,7,13)) do x
# 如果是十一月的第四个星期四, 返回 true (感恩节)
Dates.dayofweek(x) == Dates.Thursday &&
Dates.dayofweekofmonth(x) == 4 &&
Dates.month(x) == Dates.November
end
2014-11-27
类似的, ``tofirst`` 和 ``tolast`` 第一个参数为 ``DateFunction``, 但是默认的调整范围位当月, 或者可以用关键字参数指明调整范围为当年 ::
julia> Dates.tofirst(istuesday, Date(2014,7,13)) # 默认位当月
2014-07-01
julia> Dates.tofirst(istuesday, Date(2014,7,13); of=Dates.Year)
2014-01-07
julia> Dates.tolast(istuesday, Date(2014,7,13))
2014-07-29
julia> Dates.tolast(istuesday, Date(2014,7,13); of=Dates.Year)
2014-12-30
最后一个函数为 ``recur``. ``recur`` 函数是向量化的调整过程, 输入为起始和结束日期 (或者指明 ``StepRange``), 加上一个 ``DateFunction`` 来判断某个日期是否应该返回. 这种情况下, ``DateFunction`` 又被经常称为 "包括" 函数, 因为它指明了 (通过返回 true) 某个日期是否应该出现在返回的日期数组中.
::
# 匹兹堡大街清理日期; 从四月份到十一月份每月的第二个星期二
# 时间范围从2014年1月1号到2015年1月1号
julia> dr = Dates.Date(2014):Dates.Date(2015);
julia> recur(dr) do x
Dates.dayofweek(x) == Dates.Tue &&
Dates.April <= Dates.month(x) <= Dates.Nov &&
Dates.dayofweekofmonth(x) == 2
end
8-element Array{Date,1}:
2014-04-08
2014-05-13
2014-06-10
2014-07-08
2014-08-12
2014-09-09
2014-10-14
2014-11-11
更多的例子和测试可以参考 `test/dates/adjusters.jl <https://github.com/JuliaLang/julia/blob/master/test/dates/adjusters.jl>`_.
时间间隔
-------
时间间隔是从人的角度考虑的一段时间, 有时是不规则的. 想下一个月; 如果从天数上讲, 不同情况下, 它可能代表 28, 29, 30, 或者 31. 或者一年可以代表 365 或者 366 天. ``Period`` 类型是 ``Int64`` 类型的简单封装, 可以通过任何可以转换成 ``Int64`` 类型的数据构造出来, 比如 ``Year(1)`` 或者 ``Month(3.0)``. 相同类型的时间间隔的行为类似于整数 ::
julia> y1 = Dates.Year(1)
1 year
julia> y2 = Dates.Year(2)
2 years
julia> y3 = Dates.Year(10)
10 years
julia> y1 + y2
3 years
julia> div(y3,y2)
5 years
julia> y3 - y2
8 years
julia> y3 * y2
20 years
julia> y3 % y2
0 years
julia> y1 + 20
21 years
julia> div(y3,3) # 类似于整数除法
3 years
另加详细的信息可以参考 :mod:`Dates` 模块的 `API 索引 <http://docs.julialang.org/en/latest/stdlib/dates/>`_.
================================================
FILE: manual/embedding.rst
================================================
.. _man-embedding:
.. highlight:: c
**************************
嵌入式 Julia
**************************
.. As we have seen (:ref:`man-calling-c-and-fortran-code`) Julia has a simple and efficient way to call functions written .. in C. But there are situations where the opposite is needed: calling Julia function from C code. This can be used to .. integrate Julia code into a larger C/C++ project, without the need to rewrite everything in C/C++. Julia has a C API .. to make this possible. As almost all programming languages have some way to call C functions, the Julia C API can also .. be used to build further language bridges (e.g. calling Julia from Python or C#).
我们已经知道 (:ref:`man-calling-c-and-fortran-code`) Julia 可以用简单有效的方式调用 C 函数。但是有很多情况下正好相反:需要从C 调用 Julia 函数。这可以把 Julia 代码整合到更大型的 C/C++ 项目中去, 而不需要重新把所有都用C/C++写一遍。 Julia提供了给C的API来实现这一点。正如大多数语言都有方法调用 C 函数一样, Julia的 API 也可以用于搭建和其他语言之间的桥梁。
.. High-Level Embedding
高级嵌入
=====================
.. We start with a simple C program that initializes Julia and calls some Julia code::
我们从一个简单的C程序入手,它初始化Julia并且调用一些Julia的代码::
#include <julia.h>
int main(int argc, char *argv[])
{
jl_init(NULL);
JL_SET_STACK_BASE;
jl_eval_string("print(sqrt(2.0))");
return 0;
}
.. In order to build this program you have to put the path to the Julia header into the include path and link against .. ``libjulia``. For instance, when Julia is installed to ``$JULIA_DIR``, one can compile the above test program .. ``test .c`` with gcc using::
编译这个程序你需要把 Julia的头文件包含在路径内并且链接函数库 ``libjulia``。 比方说 Julia安装在 ``$JULIA_DIR``, 就可以用gcc编译::
gcc -o test -I$JULIA_DIR/include/julia -L$JULIA_DIR/usr/lib -ljulia test.c
.. Alternatively, look at the ``embedding.c`` program in the julia source tree in the ``examples/`` folder.
或者可以看看 Julia 源码里 ``example/`` 下的 ``embedding.c``。
.. The first thing that has to be done before calling any other Julia C function is to initialize Julia. This is done by .. calling ``jl_init``, which takes as argument a C string (``const char*``) to the location where Julia is installed.
.. When the argument is ``NULL``, Julia tries to determine the install location automatically.
调用Julia函数之前要先初始化Julia, 可以用 ``jl_init`` 完成,这个函数的参数是Julia安装路径,类型是 ``const char*`` 。如果没有任何参数,Julia会自动寻找Julia的安装路径。
The second statement initializes Julia's task scheduling system. This statement must appear in a function that will not return as long as calls into Julia will be made (``main`` works fine). Strictly speaking, this statement is optional, but operations that switch tasks will cause problems if it is omitted.
The third statement in the test program evaluates a Julia statement using a call to ``jl_eval_string``.
.. Converting Types
类型转换
========================
Real applications will not just need to execute expressions, but also return their values to the host program. ``jl_eval_string`` returns a ``jl_value_t*``, which is a pointer to a heap-allocated Julia object. Storing simple data types like ``Float64`` in this way is called ``boxing``, and extracting the stored primitive data is called ``unboxing``. Our improved sample program that calculates the square root of 2 in Julia and reads back the result in C looks as follows::
jl_value_t *ret = jl_eval_string("sqrt(2.0)");
if (jl_is_float64(ret)) {
double ret_unboxed = jl_unbox_float64(ret);
printf("sqrt(2.0) in C: %e \n", ret_unboxed);
}
In order to check whether ``ret`` is of a specific Julia type, we can use the ``jl_is_...`` functions. By typing ``typeof(sqrt(2.0))`` into the Julia shell we can see that the return type is ``Float64`` (``double`` in C). To convert the boxed Julia value into a C double the ``jl_unbox_float64`` function is used in the above code snippet.
Corresponding ``jl_box_...`` functions are used to convert the other way::
jl_value_t *a = jl_box_float64(3.0);
jl_value_t *b = jl_box_float32(3.0f);
jl_value_t *c = jl_box_int32(3);
As we will see next, boxing is required to call Julia functions with specific arguments.
.. Calling Julia Functions
调用 Julia 的函数
========================
While ``jl_eval_string`` allows C to obtain the result of a Julia expression, it does not allow passing arguments computed in C to Julia. For this you will need to invoke Julia functions directly, using ``jl_call``::
jl_function_t *func = jl_get_function(jl_base_module, "sqrt");
jl_value_t *argument = jl_box_float64(2.0);
jl_value_t *ret = jl_call1(func, argument);
In the first step, a handle to the Julia function ``sqrt`` is retrieved by calling ``jl_get_function``. The first argument passed to ``jl_get_function`` is a pointer to the ``Base`` module in which ``sqrt`` is defined. Then, the double value is boxed using ``jl_box_float64``. Finally, in the last step, the function is called using ``jl_call1``. ``jl_call0``, ``jl_call2``, and ``jl_call3`` functions also exist, to conveniently handle different numbers of arguments. To pass more arguments, use ``jl_call``::
jl_value_t *jl_call(jl_function_t *f, jl_value_t **args, int32_t nargs)
Its second argument ``args`` is an array of ``jl_value_t*`` arguments and ``nargs`` is the number of arguments.
.. Memory Management
内存管理
========================
..As we have seen, Julia objects are represented in C as pointers. This raises the question of who is responsible for freeing these objects.:
正如我们看到的,Julia 的对象在 C 中是以指针形式呈现的。而这也就给出了一个问题:由谁来负责释放这些对象对应的内存呢?
..Typically, Julia objects are freed by a garbage collector (GC), but the GC does not automatically know that we are holding a reference to a Julia value from C. This means the GC can free objects out from under you, rendering pointers invalid.:
一般情况下,Julia 的对象由垃圾回收机制来释放,但垃圾回收机制并不能自动获知我们正在 C 中使用 Julia 对象的引用。这意味着垃圾回收机制可能会释放我们正在使用的对象,造成该指针失效。
The GC can only run when Julia objects are allocated. Calls like ``jl_box_float64`` perform allocation, and allocation might also happen at any point in running Julia code. However, it is generally safe to use pointers in between ``jl_...`` calls. But in order to make sure that values can survive ``jl_...`` calls, we have to tell Julia that we hold a reference to a Julia value. This can be done using the ``JL_GC_PUSH`` macros::
jl_value_t *ret = jl_eval_string("sqrt(2.0)");
JL_GC_PUSH1(&ret);
// Do something with ret
JL_GC_POP();
The ``JL_GC_POP`` call releases the references established by the previous ``JL_GC_PUSH``. Note that ``JL_GC_PUSH`` is working on the stack, so it must be exactly paired with a ``JL_GC_POP`` before the stack frame is destroyed.
Several Julia values can be pushed at once using the ``JL_GC_PUSH2`` , ``JL_GC_PUSH3`` , and ``JL_GC_PUSH4`` macros. To push an array of Julia values one can use the ``JL_GC_PUSHARGS`` macro, which can be used as follows::
jl_value_t **args;
JL_GC_PUSHARGS(args, 2); // args can now hold 2 `jl_value_t*` objects
args[0] = some_value;
args[1] = some_other_value;
// Do something with args (e.g. call jl_... functions)
JL_GC_POP();
.. Manipulating the Garbage Collector
控制垃圾回收
---------------------------------------------------
..There are some functions to control the GC. In normal use cases, these should not be necessary.:
有一些函数可以帮助控制垃圾回收。在一般情况下,它们都不需要被用到。
========================= ==============================================================================
``void jl_gc_collect()`` Force a GC run
``void jl_gc_disable()`` Disable the GC
``void jl_gc_enable()`` Enable the GC
========================= ==============================================================================
.. Working with Arrays
处理数组
========================
Julia and C can share array data without copying. The next example will show how this works.
Julia arrays are represented in C by the datatype ``jl_array_t*``. Basically, ``jl_array_t`` is a struct that contains:
- Information about the datatype
- A pointer to the data block
- Information about the sizes of the array
To keep things simple, we start with a 1D array. Creating an array containing Float64 elements of length 10 is done by::
jl_value_t* array_type = jl_apply_array_type(jl_float64_type, 1);
jl_array_t* x = jl_alloc_array_1d(array_type, 10);
Alternatively, if you have already allocated the array you can generate a thin wrapper around its data::
double *existingArray = (double*)malloc(sizeof(double)*10);
jl_array_t *x = jl_ptr_to_array_1d(array_type, existingArray, 10, 0);
The last argument is a boolean indicating whether Julia should take ownership of the data. If this argument is non-zero, the GC will call ``free`` on the data pointer when the array is no longer referenced.
In order to access the data of x, we can use ``jl_array_data``::
double *xData = (double*)jl_array_data(x);
Now we can fill the array::
for(size_t i=0; i<jl_array_len(x); i++)
xData[i] = i;
Now let us call a Julia function that performs an in-place operation on ``x``::
jl_function_t *func = jl_get_function(jl_base_module, "reverse!");
jl_call1(func, (jl_value_t*)x);
By printing the array, one can verify that the elements of ``x`` are now reversed.
.. Accessing Returned Arrays
访问返回的数组
---------------------------------
If a Julia function returns an array, the return value of ``jl_eval_string`` and ``jl_call`` can be cast to a ``jl_array_t*``::
jl_function_t *func = jl_get_function(jl_base_module, "reverse");
jl_array_t *y = (jl_array_t*)jl_call1(func, (jl_value_t*)x);
Now the content of ``y`` can be accessed as before using ``jl_array_data``.
As always, be sure to keep a reference to the array while it is in use.
.. Multidimensional Arrays
高维数组
---------------------------------
Julia's multidimensional arrays are stored in memory in column-major order. Here is some code that creates a 2D array and accesses its properties::
// Create 2D array of float64 type
jl_value_t *array_type = jl_apply_array_type(jl_float64_type, 2);
jl_array_t *x = jl_alloc_array_2d(array_type, 10, 5);
// Get array pointer
double *p = (double*)jl_array_data(x);
// Get number of dimensions
int ndims = jl_array_ndims(x);
// Get the size of the i-th dim
size_t size0 = jl_array_dim(x,0);
size_t size1 = jl_array_dim(x,1);
// Fill array with data
for(size_t i=0; i<size1; i++)
for(size_t j=0; j<size0; j++)
p[j + size0*i] = i + j;
Notice that while Julia arrays use 1-based indexing, the C API uses 0-based indexing (for example in calling ``jl_array_dim``) in order to read as idiomatic C code.
.. Exceptions
异常
==========
Julia code can throw exceptions. For example, consider::
jl_eval_string("this_function_does_not_exist()");
This call will appear to do nothing. However, it is possible to check whether an exception was thrown::
if (jl_exception_occurred())
printf("%s \n", jl_typeof_str(jl_exception_occurred()));
If you are using the Julia C API from a language that supports exceptions (e.g. Python, C#, C++), it makes sense to wrap each call into libjulia with a function that checks whether an exception was thrown, and then rethrows the exception in the host language.
.. Throwing Julia Exceptions
抛出 Julia 异常
-------------------------
When writing Julia callable functions, it might be necessary to validate arguments and throw exceptions to indicate errors. A typical type check looks like::
if (!jl_is_float64(val)) {
jl_type_error(function_name, (jl_value_t*)jl_float64_type, val);
}
General exceptions can be raised using the funtions::
void jl_error(const char *str);
void jl_errorf(const char *fmt, ...);
``jl_error`` takes a C string, and ``jl_errorf`` is called like ``printf``::
jl_errorf("argument x = %d is too large", x);
where in this example ``x`` is assumed to be an integer.
================================================
FILE: manual/faq.rst
================================================
.. _man-faq:
**********
常见问题
**********
会话和 REPL
-----------
如何删除内存中的对象?
~~~~~~~~~~~~~~~~~~~~~~
Julia 没有 MATLAB 的 ``clear`` 函数;在 Julia 会话(准确来说, ``Main`` 模块)中定义了一个名字的话,它就一直在啦。
如果你很关心内存使用,你可以用占内存的小的来替换大的。例如,如果 ``A`` 是个你不需要的大数组,可以先用 ``A = 0`` 来释放内存。下一次进行垃圾回收的时候,内存就会被释放了;你也可以直接调用 ``gc()`` 来回收。
如何在会话中修改 type/immutable 的声明?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. Perhaps you've defined a type and then realize you need to add a
.. new field. If you try this at the REPL, you get the error::
有时候你定义了一种类型但是后来发现你需要添加一个新的域。当你尝试在REPL里这样做时就会出错 ::
ERROR: invalid redefinition of constant MyType
.. Types in module ``Main`` cannot be redefined.
``Main`` 模块里的类型不能被重新定义。
.. While this can be inconvenient when you are developing new code,
.. there's an excellent workaround. Modules can be replaced by
.. redefining them, and so if you wrap all your new code inside a module
.. you can redefine types and constants. You can't import the type names
.. into ``Main`` and then expect to be able to redefine them there, but
.. you can use the module name to resolve the scope. In other words,
.. while developing you might use a workflow something like this::
当你在开发新代码时这会变得极其不方便,有一个很好的办法来处理。模块是可以用重新定义的办法来替换,
所以把你的所有的代码封装在一个模块里就能够重新定义类型以及常数。你不能把类型名导入到 ``Main`` 里
再去重新定义,但是你可以用模块名来解决这个问题。换句话说,当你开发的时候可以用这样的工作流 ::
include("mynewcode.jl") # this defines a module MyModule
obj1 = MyModule.ObjConstructor(a, b)
obj2 = MyModule.somefunction(obj1)
# Got an error. Change something in "mynewcode.jl"
include("mynewcode.jl") # reload the module
obj1 = MyModule.ObjConstructor(a, b) # old objects are no longer valid, must reconstruct
obj2 = MyModule.somefunction(obj1) # this time it worked!
obj3 = MyModule.someotherfunction(obj2, c)
...
.. Functions
函数
---------
.. I passed an argument ``x`` to a function, modified it inside that function, but on the outside, the variable ``x`` is still unchanged. Why?
我把参数 ``x`` 传递给一个函数, 并在函数内修改它的值, 但是在函数外 ``x`` 的值并未发生变化, 为什么呢?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. Suppose you call a function like this::
假设你像这样调用函数::
julia> x = 10
julia> function change_value!(y) # Create a new function
y = 17
end
julia> change_value!(x)
julia> x # x is unchanged!
10
.. In Julia, any function (including ``change_value!()``) can't change the binding of a local variable. If ``x`` (in the calling scope) is bound to a immutable object (like a real number), you can't modify the object; likewise, if x is bound in the calling scope to a Dict, you can't change it to be bound to an ASCIIString.
.. But here is a thing you should pay attention to: suppose ``x`` is bound to an Array (or any other mutable type). You cannot "unbind" ``x`` from this Array. But, since an Array is a *mutable* type, you can change its content. For example::
在 Julia 里, 所有的函数(包括 ``change_value!()``) 都不能修改局部变量的所属的类。 如果 ``x`` 被函数调用时被定义为一个不可变的对象(比如实数), 就不能修改; 同样地, 如果 ``x`` 被定义为一个 ``Dict`` 对象, 你不能把它改成ASCIIString。
但是需要主要的是: 假设 ``x`` 是一个数组(或者任何可变类型)。 你不能让 ``x`` 不再代表这个数组,但由于数组是可变的对象,你能修改数组的元素::
julia> x = [1,2,3]
3-element Array{Int64,1}:
1
2
3
julia> function change_array!(A) # Create a new function
A[1] = 5
end
julia> change_array!(x)
julia> x
3-element Array{Int64,1}:
5
2
3
.. Here we created a function ``change_array!()``, that assigns ``5`` to the first element of the Array. We passed ``x`` (which was previously bound to an Array) to the function. Notice that, after the function call, ``x`` is still bound to the same Array, but the content of that Array changed.
这里我们定义了函数 ``change_array!()``, 把整数 ``5`` 分配给了数组的第一个元素。 当我们把 ``x`` 传读给这个函数时, 注意到 ``x`` 依然是同一个数组,只是数组的元素发生了变化。
.. Can I use ``using`` or ``import`` inside a function?
我能在函数中使用 ``using`` 或者 ``import`` 吗?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. No, you are not allowed to have a ``using`` or ``import`` statement inside
.. a function. If you want to import a module but only use its symbols
.. inside a specific function or set of functions, you have two options:
不行, 在函数中不能使用 ``using`` 或 ``import``。 如果你要导入一个模块但只是在某些函数里使用,你有两种方案::
1. 使用 ``import`` ::
import Foo
function bar(...)
... refer to Foo symbols via Foo.baz ...
end
.. This loads the module Foo and defines a variable ``Foo`` that refers
.. to the module, but does not import any of the other symbols from the
.. module into the current namespace. You refer to the ``Foo`` symbols by
.. their qualified names ``Foo.bar`` etc.
这把 Foo 模块载入并且用变量 ``Foo`` 代表这个模块,但是这并没有导入任何东西到当前命名空间。你还是需要像 ``Foo.bar`` 这样使用模块内的东西。
.. 2. Wrap your function in a module::
2. 把函数封装到模块里::
module Bar
export bar
using Foo
function bar(...)
... refer to Foo.baz as simply baz ....
end
end
using Bar
.. This imports all the symbols from Foo, but only inside the module Bar.
这样会把 Foo 的内容全部导入,但仅仅导入到模块 Bar 里。
类型,类型声明和构造方法
------------------------
.. _man-type-stable:
什么是“类型稳定”?
~~~~~~~~~~~~~~~~~~
.. It means that the type of the output is predictable from the types
.. of the inputs. In particular, it means that the type of the output
.. cannot vary depending on the *values* of the inputs. The following
.. code is *not* type-stable::
这意味着输出的类型是可以由输入类型预测出来。特别地,这表示输出的类型不能因输入的值的变化而
变化。下面这段代码 *不是* 类型稳定的 ::
function unstable(flag::Bool)
if flag
return 1
else
return 1.0
end
end
.. It returns either an ``Int`` or a ``Float64`` depending on the value of its
.. argument. Since Julia can't predict the return type of this function at
.. compile-time, any computation that uses it will have to guard against both
.. types possibly occurring, making generation of fast machine code difficult.
这段代码视参数的值的不同而返回一个 ``Int`` 或是 ``Float64``。 因为 Julia 无法在编译时预测
函数返回值类型, 任何使用这个函数的计算都得考虑这两种可能的返回类型, 这样很难生成快速的机器码。
.. _man-domain-error:
.. Why does Julia give a ``DomainError`` for certain seemingly-sensible operations?
为什么看似合理的运算 Julia还是返回 ``DomainError`` ?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. Certain operations make mathematical sense but result in errors::
有些运算数学上讲得通但是会产生错误::
julia> sqrt(-2.0)
ERROR: DomainError
in sqrt at math.jl:128
julia> 2^-5
ERROR: DomainError
in power_by_squaring at intfuncs.jl:70
in ^ at intfuncs.jl:84
.. This behavior is an inconvenient consequence of the requirement for
.. type-stability. In the case of ``sqrt``, most users want
.. ``sqrt(2.0)`` to give a real number, and would be unhappy if it
.. produced the complex number ``1.4142135623730951 + 0.0im``. One could
.. write the ``sqrt`` function to switch to a complex-valued output only
.. when passed a negative number (which is what ``sqrt`` does in some
.. other languages), but then the result would not be `type-stable
.. <#man-type-stable>`_ and the ``sqrt`` function would have poor
.. performance.
这时由类型稳定造成的。对于 ``sqrt``, 大多数用户会用 ``sqrt(2.0)`` 得到一个实数而不是得到一个复数 ``1.4142135623730951 + 0.0im`` 。 也可以把 ``sqrt`` 写成当参数为负的时候返回复数, 但是这将不再是 `类型稳定 <#man-type-stable>`_ 而且 ``sqrt`` 会变的很慢。
.. In these and other cases, you can get the result you want by choosing
.. an *input type* that conveys your willingness to accept an *output type* in
.. which the result can be represented::
在这些情况下,你可以选择 *输入类型* 来得到想要的 *输出类型* ::
julia> sqrt(-2.0+0im)
0.0 + 1.4142135623730951im
julia> 2.0^-5
0.03125
Why does Julia use native machine integer arithmetic?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Julia uses machine arithmetic for integer computations. This means that the range of ``Int`` values is bounded and wraps around at either end so that adding, subtracting and multiplying integers can overflow or underflow, leading to some results that can be unsettling at first::
julia> typemax(Int)
9223372036854775807
julia> ans+1
-9223372036854775808
julia> -ans
-9223372036854775808
julia> 2*ans
0
Clearly, this is far from the way mathematical integers behave, and you might
think it less than ideal for a high-level programming language to expose this
to the user. For numerical work where efficiency and transparency are at a
premium, however, the alternatives are worse.
One alternative to consider would be to check each integer operation for
overflow and promote results to bigger integer types such as ``Int128`` or
``BigInt`` in the case of overflow. Unfortunately, this introduces major
overhead on every integer operation (think incrementing a loop counter) – it
requires emitting code to perform run-time overflow checks after arithmetic
instructions and braches to handle potential overflows. Worse still, this
would cause every computation involving integers to be type-unstable. As we
mentioned above, `type-stability is crucial <#man-type-stable>`_ for effective
generation of efficient code. If you can't count on the results of integer
operations being integers, it's impossible to generate fast, simple code the
way C and Fortran compilers do.
A variation on this approach, which avoids the appearance of type instabilty is to merge the ``Int`` and ``BigInt`` types into a single hybrid integer type, that internally changes representation when a result no longer fits into the size of a machine integer. While this superficially avoids type-instability at the level of Julia code, it just sweeps the problem under the rug by foisting all of the same difficulties onto the C code implementing this hybrid integer type. This approach *can* be made to work and can even be made quite fast in many cases, but has several drawbacks. One problem is that the in-memory representation of integers and arrays of integers no longer match the natural representation used by C, Fortran and other languages with native machine integers. Thus, to interoperate with those languages, we would ultimately need to introduce native integer types anyway. Any unbounded representation of integers cannot have a fixed number of bits, and thus cannot be stored inline in an array with fixed-size slots – large integer values will always require separate heap-allocated storage. And of course, no matter how clever a hybrid integer implementation one uses, there are always performance traps – situations where performance degrades unexpectedly. Complex representation, lack of interoperability with C and Fortran, the inability to represent integer arrays without additional heap storage, and unpredictable performance characteristics make even the cleverest hybrid integer implementations a poor choice for high-performance numerical work.
An alternative to using hybrid integers or promoting to BigInts is to use
saturating integer arithmetic, where adding to the largest integer value
leaves it unchanged and likewise for subtracting from the smallest integer
value. This is precisely what Matlab™ does::
>> int64(9223372036854775807)
ans =
9223372036854775807
>> int64(9223372036854775807) + 1
ans =
9223372036854775807
>> int64(-9223372036854775808)
ans =
-9223372036854775808
>> int64(-9223372036854775808) - 1
ans =
-9223372036854775808
At first blush, this seems reasonable enough since 9223372036854775807 is much closer to 9223372036854775808 than -9223372036854775808 is and integers are still represented with a fixed size in a natural way that is compatible with C and Fortran. Saturated integer arithmetic, however, is deeply problematic. The first and most obvious issue is that this is not the way machine integer arithmetic works, so implementing saturated operations requires emiting instructions after each machine integer operation to check for underflow or overflow and replace the result with ``typemin(Int)`` or ``typemax(Int)`` as appropriate. This alone expands each integer operation from a single, fast instruction into half a dozen instructions, probably including branches. Ouch. But it gets worse – saturating integer arithmetic isn't associative. Consider this Matlab computation::
>> n = int64(2)^62
4611686018427387904
>> n + (n - 1)
9223372036854775807
>> (n + n) - 1
9223372036854775806
This makes it hard to write many basic integer algorithms since a lot of
common techniques depend on the fact that machine addition with overflow *is*
associative. Consider finding the midpoint between integer values ``lo`` and
``hi`` in Julia using the expression ``(lo + hi) >>> 1``::
julia> n = 2^62
4611686018427387904
julia> (n + 2n) >>> 1
6917529027641081856
See? No problem. That's the correct midpoint between 2^62 and 2^63, despite
the fact that ``n + 2n`` is -4611686018427387904. Now try it in Matlab::
>> (n + 2*n)/2
ans =
4611686018427387904
Oops. Adding a ``>>>`` operator to Matlab wouldn't help, because saturation
that occurs when adding ``n`` and ``2n`` has already destroyed the information
necessary to compute the correct midpoint.
Not only is lack of associativity unfortunate for programmers who cannot rely
it for techniques like this, but it also defeats almost anything compilers
might want to do to optimize integer arithmetic. For example, since Julia
integers use normal machine integer arithmetic, LLVM is free to aggressively
optimize simple little functions like ``f(k) = 5k-1``. The machine code for
this function is just this::
julia> code_native(f,(Int,))
.section __TEXT,__text,regular,pure_instructions
Filename: none
Source line: 1
push RBP
mov RBP, RSP
Source line: 1
lea RAX, QWORD PTR [RDI + 4*RDI - 1]
pop RBP
ret
The actual body of the function is a single ``lea`` instruction, which
computes the integer multiply and add at once. This is even more beneficial
when ``f`` gets inlined into another function::
julia> function g(k,n)
for i = 1:n
k = f(k)
end
return k
end
g (generic function with 2 methods)
julia> code_native(g,(Int,Int))
.section __TEXT,__text,regular,pure_instructions
Filename: none
Source line: 3
push RBP
mov RBP, RSP
test RSI, RSI
jle 22
mov EAX, 1
Source line: 3
lea RDI, QWORD PTR [RDI + 4*RDI - 1]
inc RAX
cmp RAX, RSI
Source line: 2
jle -17
Source line: 5
mov RAX, RDI
pop RBP
ret
Since the call to ``f`` gets inlined, the loop body ends up being just a
single ``lea`` instruction. Next, consider what happens if we make the number
of loop iterations fixed::
julia> function g(k)
for i = 1:10
k = f(k)
end
return k
end
g (generic function with 2 methods)
julia> code_native(g,(Int,))
.section __TEXT,__text,regular,pure_instructions
Filename: none
Source line: 3
push RBP
mov RBP, RSP
Source line: 3
imul RAX, RDI, 9765625
add RAX, -2441406
Source line: 5
pop RBP
ret
Because the compiler knows that integer addition and multiplication are
associative and that multiplication distributes over addition – neither of
which is true of saturating arithmetic – it can optimize the entire loop down
to just a multiply and an add. Saturated arithmetic completely defeats this
kind of optimization since associativity and distributivity can fail at each
loop iteration, causing different outcomes depending on which iteration the
failure occurs in. The compiler can unroll the loop, but it cannot
algebraically reduce multiple operations into fewer equivalent operations.
Saturated integer arithmetic is just one example of a really poor choice of
language semantics that completely prevents effective performance
optimization. There are many things that are difficult about C programming,
but integer overflow is *not* one of them – especially on 64-bit systems. If
my integers really might get bigger than 2^63-1, I can easily predict that. Am
I looping over a number of actual things that are stored in the computer? Then
it's not going to get that big. This is guaranteed, since I don't have that
much memory. Am I counting things that occur in the real world? Unless they're
grains of sand or atoms in the universe, 2^63-1 is going to be plenty big. Am
I computing a factorial? Then sure, they might get that big – I should use a
``BigInt``. See? Easy to distinguish.
.. _man-abstract-fields:
How do "abstract" or ambiguous fields in types interact with the compiler?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Types can be declared without specifying the types of their fields:
.. doctest::
julia> type MyAmbiguousType
a
end
This allows ``a`` to be of any type. This can often be useful, but it
does have a downside: for objects of type ``MyAmbiguousType``, the
compiler will not be able to generate high-performance code. The
reason is that the compiler uses the types of objects, not their
values, to determine how to build code. Unfortunately, very little can
be inferred about an object of type ``MyAmbiguousType``:
.. doctest::
julia> b = MyAmbiguousType("Hello")
MyAmbiguousType("Hello")
julia> c = MyAmbiguousType(17)
MyAmbiguousType(17)
julia> typeof(b)
MyAmbiguousType (constructor with 1 method)
julia> typeof(c)
MyAmbiguousType (constructor with 1 method)
``b`` and ``c`` have the same type, yet their underlying
representation of data in memory is very different. Even if you stored
just numeric values in field ``a``, the fact that the memory
representation of a ``Uint8`` differs from a ``Float64`` also means
that the CPU needs to handle them using two different kinds of
instructions. Since the required information is not available in the
type, such decisions have to be made at run-time. This slows
performance.
You can do better by declaring the type of ``a``. Here, we are focused
on the case where ``a`` might be any one of several types, in which
case the natural solution is to use parameters. For example:
.. doctest::
julia> type MyType{T<:FloatingPoint}
a::T
end
This is a better choice than
.. doctest::
julia> type MyStillAmbiguousType
a::FloatingPoint
end
because the first version specifies the type of ``a`` from the type of
the wrapper object. For example:
.. doctest::
julia> m = MyType(3.2)
MyType{Float64}(3.2)
julia> t = MyStillAmbiguousType(3.2)
MyStillAmbiguousType(3.2)
julia> typeof(m)
MyType{Float64} (constructor with 1 method)
julia> typeof(t)
MyStillAmbiguousType (constructor with 2 methods)
The type of field ``a`` can be readily determined from the type of
``m``, but not from the type of ``t``. Indeed, in ``t`` it's possible
to change the type of field ``a``:
.. doctest::
julia> typeof(t.a)
Float64
julia> t.a = 4.5f0
4.5f0
julia> typeof(t.a)
Float32
In contrast, once ``m`` is constructed, the type of ``m.a`` cannot
change:
.. doctest::
julia> m.a = 4.5f0
4.5
julia> typeof(m.a)
Float64
The fact that the type of ``m.a`` is known from ``m``'s type---coupled
with the fact that its type cannot change mid-function---allows the
compiler to generate highly-optimized code for objects like ``m`` but
not for objects like ``t``.
Of course, all of this is true only if we construct ``m`` with a
concrete type. We can break this by explicitly constructing it with
an abstract type:
.. doctest::
julia> m = MyType{FloatingPoint}(3.2)
MyType{FloatingPoint}(3.2)
julia> typeof(m.a)
Float64
julia> m.a = 4.5f0
4.5f0
julia> typeof(m.a)
Float32
For all practical purposes, such objects behave identically to those
of ``MyStillAmbiguousType``.
It's quite instructive to compare the sheer amount code generated for
a simple function
::
func(m::MyType) = m.a+1
using
::
code_llvm(func,(MyType{Float64},))
code_llvm(func,(MyType{FloatingPoint},))
code_llvm(func,(MyType,))
For reasons of length the results are not shown here, but you may wish
to try this yourself. Because the type is fully-specified in the first
case, the compiler doesn't need to generate any code to resolve the
type at run-time. This results in shorter and faster code.
.. _man-abstract-container-type:
如何声明“抽象容器类型”的域
~~~~~~~~~~~~~~~~~~~~~~~~~~
The same best practices that apply in the `previous section
<#man-abstract-fields>`_ also work for container types:
.. doctest::
julia> type MySimpleContainer{A<:AbstractVector}
a::A
end
julia> type MyAmbiguousContainer{T}
a::AbstractVector{T}
end
For example:
.. doctest::
julia> c = MySimpleContainer(1:3);
julia> typeof(c)
MySimpleContainer{UnitRange{Int64}} (constructor with 1 method)
julia> c = MySimpleContainer([1:3]);
julia> typeof(c)
MySimpleContainer{Array{Int64,1}} (constructor with 1 method)
julia> b = MyAmbiguousContainer(1:3);
julia> typeof(b)
MyAmbiguousContainer{Int64} (constructor with 1 method)
julia> b = MyAmbiguousContainer([1:3]);
julia> typeof(b)
MyAmbiguousContainer{Int64} (constructor with 1 method)
For ``MySimpleContainer``, the object is fully-specified by its type
and parameters, so the compiler can generate optimized functions. In
most instances, this will probably suffice.
While the compiler can now do its job perfectly well, there are cases
where *you* might wish that your code could do different things
depending on the *element type* of ``a``. Usually the best way to
achieve this is to wrap your specific operation (here, ``foo``) in a
separate function::
function sumfoo(c::MySimpleContainer)
s = 0
for x in c.a
s += foo(x)
end
s
end
foo(x::Integer) = x
foo(x::FloatingPoint) = round(x)
This keeps things simple, while allowing the compiler to generate
optimized code in all cases.
However, there are cases where you may need to declare different
versions of the outer function for different element types of
``a``. You could do it like this::
function myfun{T<:FloatingPoint}(c::MySimpleContainer{Vector{T}})
...
end
function myfun{T<:Integer}(c::MySimpleContainer{Vector{T}})
...
end
This works fine for ``Vector{T}``, but we'd also have to write
explicit versions for ``UnitRange{T}`` or other abstract types. To
prevent such tedium, you can use two parameters in the declaration of
``MyContainer``::
type MyContainer{T, A<:AbstractVector}
a::A
end
MyContainer(v::AbstractVector) = MyContainer{eltype(v), typeof(v)}(v)
julia> b = MyContainer(1.3:5);
julia> typeof(b)
MyContainer{Float64,UnitRange{Float64}}
Note the somewhat surprising fact that ``T`` doesn't appear in the
declaration of field ``a``, a point that we'll return to in a moment.
With this approach, one can write functions such as::
function myfunc{T<:Integer, A<:AbstractArray}(c::MyContainer{T,A})
return c.a[1]+1
end
# Note: because we can only define MyContainer for
# A<:AbstractArray, and any unspecified parameters are arbitrary,
# the previous could have been written more succinctly as
# function myfunc{T<:Integer}(c::MyContainer{T})
function myfunc{T<:FloatingPoint}(c::MyContainer{T})
return c.a[1]+2
end
function myfunc{T<:Integer}(c::MyContainer{T,Vector{T}})
return c.a[1]+3
end
julia> myfunc(MyContainer(1:3))
2
julia> myfunc(MyContainer(1.0:3))
3.0
julia> myfunc(MyContainer([1:3]))
4
As you can see, with this approach it's possible to specialize on both
the element type ``T`` and the array type ``A``.
However, there's one remaining hole: we haven't enforced that ``A``
has element type ``T``, so it's perfectly possible to construct an
object like this::
julia> b = MyContainer{Int64, UnitRange{Float64}}(1.3:5);
julia> typeof(b)
MyContainer{Int64,UnitRange{Float64}}
To prevent this, we can add an inner constructor::
type MyBetterContainer{T<:Real, A<:AbstractVector}
a::A
MyBetterContainer(v::AbstractVector{T}) = new(v)
end
MyBetterContainer(v::AbstractVector) = MyBetterContainer{eltype(v),typeof(v)}(v)
julia> b = MyBetterContainer(1.3:5);
julia> typeof(b)
MyBetterContainer{Float64,UnitRange{Float64}}
julia> b = MyBetterContainer{Int64, UnitRange{Float64}}(1.3:5);
ERROR: no method MyBetterContainer(UnitRange{Float64},)
The inner constructor requires that the element type of ``A`` be ``T``.
Nothingness and missing values
------------------------------
How does "null" or "nothingness" work in Julia?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Unlike many languages (for example, C and Java), Julia does not have a
"null" value. When a reference (variable, object field, or array element)
is uninitialized, accessing it will immediately throw an error. This
situation can be detected using the ``isdefined`` function.
Some functions are used only for their side effects, and do not need to
return a value. In these cases, the convention is to return the value
``nothing``, which is just a singleton object of type ``Nothing``. This
is an ordinary type with no fields; there is nothing special about it
except for this convention, and that the REPL does not print anything
for it. Some language constructs that would not otherwise have a value
also yield ``nothing``, for example ``if false; end``.
Note that ``Nothing`` (uppercase) is the type of ``nothing``, and should
only be used in a context where a type is required (e.g. a declaration).
You may occasionally see ``None``, which is quite different. It is the
empty (or "bottom") type, a type with no values and no subtypes (except
itself). You will generally not need to use this type.
The empty tuple (``()``) is another form of nothingness. But, it should not
really be thought of as nothing but rather a tuple of zero values.
Julia 发行版
------------
Do I want to use a release, beta, or nightly version of Julia?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
You may prefer the release version of Julia if you are looking for a stable code base. Releases generally occur every 6 months, giving you a stable platform for writing code.
You may prefer the beta version of Julia if you don't mind being slightly behind the latest bugfixes and changes, but find the slightly faster rate of changes more appealing. Additionally, these binaries are tested before they are published to ensure they are fully functional.
You may prefer the nightly version of Julia if you want to take advantage of the latest updates to the language, and don't mind if the version available today occasionally doesn't actually work.
Finally, you may also consider building Julia from source for yourself. This option is mainly for those individuals who are comfortable at the command line, or interested in learning. If this describes you, you may also be interested in reading our `guidelines for contributing`__.
__ https://github.com/JuliaLang/julia/blob/master/CONTRIBUTING.md
Links to each of these download types can be found on the download page at http://julialang.org/downloads/. Note that not all versions of Julia are available for all platforms.
何时移除舍弃的函数?
~~~~~~~~~~~~~~~~~~~~
Deprecated functions are removed after the subsequent release. For example, functions marked as deprecated in the 0.1 release will not be available starting with the 0.2 release.
开发 Julia
----------
How do I debug julia's C code? (running the julia REPL from within a debugger like gdb)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
First, you should build the debug version of julia with ``make
debug``. Below, lines starting with ``(gdb)`` mean things you should
type at the gdb prompt.
From the shell
^^^^^^^^^^^^^^
The main challenge is that Julia and gdb each need to have their own
terminal, to allow you to interact with them both. One approach is to
use gdb's ``attach`` functionality to debug an already-running julia
session. However, on many systems you'll need root access to get this
to work. What follows is a method that can be implemented with just
user-level permissions.
The first time you do this, you'll need to define a script, here
called ``oterm``, containing the following lines::
ps
sleep 600000
Make it executable with ``chmod +x oterm``.
Now:
- From a shell (called shell 1), type ``xterm -e oterm &``. You'll see
a new window pop up; this will be called terminal 2.
- From within shell 1, ``gdb julia-debug``. You can find this
executable within ``julia/usr/bin``.
- From within shell 1, ``(gdb) tty /dev/pts/#`` where ``#`` is the
number shown after ``pts/`` in terminal 2.
- From within shell 1, ``(gdb) run``
- From within terminal 2, issue any preparatory commands in Julia that
you need to get to the step you want to debug
- From within shell 1, hit Ctrl-C
- From within shell 1, insert your breakpoint, e.g., ``(gdb) b codegen.cpp:2244``
- From within shell 1, ``(gdb) c`` to resume execution of julia
- From within terminal 2, issue the command that you want to
debug. Shell 1 will stop at your breakpoint.
Within emacs
^^^^^^^^^^^^
- ``M-x gdb``, then enter ``julia-debug`` (this is easiest from
within julia/usr/bin, or you can specify the full path)
- ``(gdb) run``
- Now you'll see the Julia prompt. Run any commands in Julia you need
to get to the step you want to debug.
- Under emacs' "Signals" menu choose BREAK---this will return you to the ``(gdb)`` prompt
- Set a breakpoint, e.g., ``(gdb) b codegen.cpp:2244``
- Go back to the Julia prompt via ``(gdb) c``
- Execute the Julia command you want to see running.
================================================
FILE: manual/functions.rst
================================================
.. _man-functions:
******
函数
******
Julia 中的函数是将一系列参数组成的元组映设到一个返回值的对象,Julia 的函数不是纯的数学式函数,有些函数可以改变或者影响程序的全局状态。Julia 中定义函数的基本语法为:
.. testcode::
function f(x,y)
x + y
end
.. testoutput::
:hide:
f (generic function with 1 method)
Julia 中可以精炼地定义函数。上述传统的声明语法,等价于下列紧凑的“赋值形式”: ::
f(x,y) = x + y
对于赋值形式,函数体通常是单表达式,但也可以为复合表达式(详见 :ref:`man-compound-expressions` )。Julia 中常见这种短小简单的函数定义。短函数语法相对而言更方便输入和阅读。
使用圆括号来调用函数:
.. doctest::
julia> f(2,3)
5
没有圆括号时, ``f`` 表达式指向的是函数对象,这个函数对象可以像值一样被传递:
.. doctest::
julia> g = f;
julia> g(2,3)
5
调用函数有两种方法:使用特定函数名的特殊运算符语法(详见后面 `函数运算符 <#operators-are-functions>`_ ),或者使用 ``apply`` 函数:
.. doctest::
julia> apply(f,2,3)
5
``apply`` 函数把第一个参数当做函数对象,应用在后面的参数上。
和变量名称一样, 函数名称也可以使用 Unicode 字符::
julia> ∑(x,y) = x + y
∑ (generic function with 1 method)
参数传递行为
------------
Julia 函数的参数遵循 "pass-by-sharing" 的惯例,即不传递值,而是传递引用。函数参数本身,有点儿像新变量 *绑定* (引用值的新位置),但它们引用的值与传递的值完全相同。对可变值(如数组)的修改,会影响其它函数。
.. _man-return-keyword:
``return`` 关键字
-----------------
函数返回值通常是函数体中最后一个表达式的值。上一节中 ``f`` 是表达式 ``x + y`` 的值。在 C 和大部分命令式语言或函数式语言中, ``return`` 关键字使得函数在计算完该表达式的值后立即返回: ::
function g(x,y)
return x * y
x + y
end
对比下列两个函数: ::
f(x,y) = x + y
function g(x,y)
return x * y
x + y
end
julia> f(2,3)
5
julia> g(2,3)
6
在纯线性函数体,比如 ``g`` 中,不需要使用 ``return`` ,它不会计算表达式 ``x + y`` 。可以把 ``x * y`` 作为函数的最后一个表达式,并省略 ``return`` 。只有涉及其它控制流时, ``return`` 才有用。下例计算直角三角形的斜边长度,其中直角边为 *x* 和 *y* ,为避免溢出: ::
function hypot(x,y)
x = abs(x)
y = abs(y)
if x > y
r = y/x
return x*sqrt(1+r*r)
end
if y == 0
return zero(x)
end
r = x/y
return y*sqrt(1+r*r)
end
最后一行的 ``return`` 可以省略。
.. _man-operators-are-functions:
函数运算符
----------
Julia 中,大多数运算符都是支持特定语法的函数。 ``&&`` 、 ``||`` 等短路运算是例外,它们不是函数,因为 :ref:`短路求值 <man-short-circuit-evaluation>` 先算前面的值,再算后面的值。 对于函数运算符,可以像其它函数一样,把参数列表用圆括号括起来,作为函数运算符的参数:
.. doctest::
julia> 1 + 2 + 3
6
julia> +(1,2,3)
6
中缀形式与函数形式完全等价,事实上,前者被内部解析为函数调用的形式。可以像对其它函数一样,对 ``+`` 、 ``*`` 等运算符进行赋值、传递:
.. doctest:: f-plus
julia> f = +;
julia> f(1,2,3)
6
但是,这时 ``f`` 函数不支持中缀表达式。
特殊名字的运算符
----------------
有一些表达式调用特殊名字的运算符:
=================== ==============
表达式 调用
=================== ==============
``[A B C ...]`` ``hcat``
``[A, B, C, ...]`` ``vcat``
``[A B; C D; ...]`` ``hvcat``
``A'`` ``ctranspose``
``A.'`` ``transpose``
``1:n`` ``colon``
``A[i]`` ``getindex``
``A[i]=x`` ``setindex!``
=================== ==============
这些函数都存在于 ``Base.Operators`` 模块中。
.. _man-anonymous-functions:
匿名函数
--------
Julia 中函数是 `第一类对象 <http://zh.wikipedia.org/zh-cn/%E7%AC%AC%E4%B8%80%E9%A1%9E%E7%89%A9%E4%BB%B6>`_ ,可以被赋值给变量,可以通过赋值后的变量来调用函数, 还可以当做参数和返回值,甚至可以被匿名构造:
.. doctest::
julia> x -> x^2 + 2x - 1
(anonymous function)
上例构造了一个匿名函数,输入一个参数 *x* ,返回多项式 *x*\ ^2 + 2\ *x* - 1 的值。匿名函数的主要作用是把它传递给接受其它函数作为参数的函数。最经典的例子是 ``map`` 函数,它将函数应用在数组的每个值上,返回结果数组:
.. doctest::
julia> map(round, [1.2,3.5,1.7])
3-element Array{Float64,1}:
1.0
4.0
2.0
``map`` 的第一个参数可以是非匿名函数。但是大多数情况,不存在这样的函数时,匿名函数就可以简单地构造单用途的函数对象,而不需要名字:
.. doctest::
julia> map(x -> x^2 + 2x - 1, [1,3,-1])
3-element Array{Int64,1}:
2
14
-2
匿名函数可以通过类似 ``(x,y,z)->2x+y-z`` 的语法接收多个参数。无参匿名函数则类似于 ``()->3`` 。无参匿名函数可以“延迟”计算,做这个用处时,代码被封装进无参函数,以后可以通过把它命名为 ``f()`` 来引入。
多返回值
--------
Julia 中可以通过返回多元组来模拟返回多值。但是,多元组并不需要圆括号来构造和析构,因此造成了可以返回多值的假象。下例返回一对儿值:
.. doctest::
julia> function foo(a,b)
a+b, a*b
end;
如果在交互式会话中调用这个函数,但不将返回值赋值出去,会看到返回的是多元组:
.. doctest::
julia> foo(2,3)
(5,6)
Julia 支持简单的多元组“析构”来给变量赋值:
.. doctest::
julia> x, y = foo(2,3);
julia> x
5
julia> y
6
也可以通过 ``return`` 来返回: ::
function foo(a,b)
return a+b, a*b
end
这与之前定义的 ``foo`` 结果相同。
变参函数
--------
函数的参数列表如果可以为任意个数,有时会非常方便。这种函数被称为“变参”函数,是“参数个数可变”的简称。可以在最后一个参数后紧跟省略号 ``...`` 来定义变参函数:
.. doctest::
julia> bar(a,b,x...) = (a,b,x)
bar (generic function with 1 method)
变量 ``a`` 和 ``b`` 是前两个普通的参数,变量 ``x`` 是尾随的可迭代的参数集合,其参数个数为 0 或多个:
.. doctest::
julia> bar(1,2)
(1,2,())
julia> bar(1,2,3)
(1,2,(3,))
julia> bar(1,2,3,4)
(1,2,(3,4))
julia> bar(1,2,3,4,5,6)
(1,2,(3,4,5,6))
上述例子中, ``x`` 是传递给 ``bar`` 的尾随的值多元组。
函数调用时,也可以使用 ``...`` :
.. doctest::
julia> x = (3,4)
(3,4)
julia> bar(1,2,x...)
(1,2,(3,4))
上例中,多元组的值完全按照变参函数的定义进行内插,也可以不完全遵守其函数定义来调用:
.. doctest::
julia> x = (2,3,4)
(2,3,4)
julia> bar(1,x...)
(1,2,(3,4))
julia> x = (1,2,3,4)
(1,2,3,4)
julia> bar(x...)
(1,2,(3,4))
被内插的对象也可以不是多元组:
.. doctest::
julia> x = [3,4]
2-element Array{Int64,1}:
3
4
julia> bar(1,2,x...)
(1,2,(3,4))
julia> x = [1,2,3,4]
4-element Array{Int64,1}:
1
2
3
4
julia> bar(x...)
(1,2,(3,4))
原函数也可以不是变参函数(大多数情况下,应该写成变参函数): ::
baz(a,b) = a + b
julia> args = [1,2]
2-element Int64 Array:
1
2
julia> baz(args...)
3
julia> args = [1,2,3]
3-element Int64 Array:
1
2
3
julia> baz(args...)
no method baz(Int64,Int64,Int64)
但如果输入的参数个数不对,函数调用会失败。
可选参数
--------
很多时候,函数参数都有默认值。例如,库函数 ``parseint(num,base)`` 把字符串解析为某个进制的数。 ``base`` 参数默认为 ``10`` 。这种情形可以写为: ::
function parseint(num, base=10)
###
end
这时,调用函数时,参数可以是一个或两个。当第二个参数未指明时,自动传递 ``10`` :
.. doctest::
julia> parseint("12",10)
12
julia> parseint("12",3)
5
julia> parseint("12")
12
可选参数很方便参数个数不同的多方法定义(详见 :ref:`man-methods` )。
关键字参数
----------
有些函数的参数个数很多,或者有很多行为。很难记住如何调用这种函数。关键字参数,允许通过参数名来区分参数,便于使用、扩展这些复杂接口。
例如,函数 ``plot`` 用于画出一条线。此函数有许多可选项,控制线的类型、宽度、颜色等。如果它接收关键字参数,当我们要指明线的宽度时,可以调用 ``plot(x, y, width=2)`` 之类的形式。这样的调用方法给参数添加了标签,便于阅读;也可以按任何顺序传递部分参数。
使用关键字参数的函数,在函数签名中使用分号来定义: ::
function plot(x, y; style="solid", width=1, color="black")
###
end
额外的关键字参数,可以像变参函数中一样,使用 ``...`` 来匹配: ::
function f(x; y=0, args...)
###
end
在函数 ``f`` 内部, ``args`` 可以是 ``(key,value)`` 多元组的集合,其中
``key`` 是符号。可以在函数调用时使用分号来传递这个集合, 如 ``f(x, z=1;
args...)``. 这种情况下也可以使用字典。
关键字参数的默认值仅在必要的时候从左至右地被求值(当对应的关键字参数没有被传递),所以默认的(关键字参数的)表达式可以调用在它之前的关键字参数。
默认值的求值作用域
----------------
可选参数和关键字参数的区别在于它们的默认值是怎样被求值的。当可选的参数被求值时,只有在它 *之前的* 的参数在作用域之内; 与之相对的, 当关键字参数的默认值被计算时, *所有的* 参数都是在作用域之内。比如,定义函数::
function f(x, a=b, b=1)
###
end
在 ``a=b`` 中的 ``b`` 指的是该函数的作用域之外的 ``b`` ,而不是接下来
的参数 ``b``。然而,如果 ``a`` 和 ``b`` 都是关键字参数,那么它们都将在
生成在同一个作用域上, ``a=b`` 中的 b 指向的是接下来的参数 ``b`` (遮蔽
了任何外层空间的 ``b``), 并且 ``a=b`` 会得到未定义变量的错误 (因为默认
参数的表达式是自左而右的求值的, ``b`` 并没有被赋值)。
函数参数的块语法
----------------
将函数作为参数传递给其它函数,当行数较多时,有时不太方便。下例在多行函数中调用 ``map`` : ::
map(x->begin
if x < 0 && iseven(x)
return 0
elseif x == 0
return 1
else
return x
end
end,
[A, B, C])
Julia 提供了保留字 ``do`` 来重写这种代码,使之更清晰: ::
map([A, B, C]) do x
if x < 0 && iseven(x)
return 0
elseif x == 0
return 1
else
return x
end
end
``do x`` 语法会建立一个以 ``x`` 为参数的匿名函数,并将其作为第一个参数传递给
``map`` . 类似地, ``do a,b`` 会创造一个含双参数的匿名函数,而一个普通的
``do`` 将声明其后是一个形式为 ``() -> ...`` 的匿名函数。
这些参数的初始化方式取决于"outer"函数;这里 ``map`` 将依次将 ``x`` 设为 ``A`` , ``B``, ``C``,
各自调用匿名函数,效果就像使用语法 ``map(func, [A, B, C])`` 一样。
这一语法使得函数使用更为容易,函数调用就像普通的代码块,从而有效拓展了这一语言。
也有许多不同于 ``map`` 的使用方法存在,例如管理系统状态。例如,有一个版本的 ``open`` 语法可
确保所打开的文件最终被关闭::
open("outfile", "w") do io
write(io, data)
end
这一功能由如下的定义所实现::
function open(f::Function, args...)
io = open(args...)
try
f(io)
finally
close(io)
end
end
相较 ``map`` 的例子而言,这里 ``io`` 由 ``open("outfile", "w")`` 返回的的结果初始化。
该串流之后被传递至负责写入的匿名函数;最终,``open`` 函数将保证你的函数退出之后该串流被正确关闭。
``try/finally`` 结构将在 :ref:`man-control-flow` 部分介绍。
``do`` 块结构帮助检查文档以确定用户函数的参数如何被初始化。
延伸阅读
---------------
我们有必要在这里说明:以上对于函数定义的解释还远远不够。Julia 有一个复杂的类型系统可以运行使用多重派发。上面并没有给出相关的例子。
- 类型系统的介绍 :ref:`man-types`
- 以方法的形式定义函数,并在运行时被多重派发的类型在这里有描述 :ref:`man-methods`
================================================
FILE: manual/getting-started.md
================================================
# 起步
<!-- # Getting Started -->
不管是用编译好的程序,还是自己从源码编译,安装 Julia 都是一件很简单的事情。按照 [https://julialang.org/downloads/](https://julialang.org/downloads/) 的提示就可以轻松下载并安装 Julia。
<!-- Julia installation is straightforward, whether using precompiled binaries or compiling from source.
Download and install Julia by following the instructions at [https://julialang.org/downloads/](https://julialang.org/downloads/). -->
启动一个交互式会话(也被叫做 REPL)是学习和尝试 Julia 最简单的方法。双击 Julia 的可执行文件或是从命令行运行 `julia` 就可以启动:
<!-- The easiest way to learn and experiment with Julia is by starting an interactive session (also
known as a read-eval-print loop or "repl") by double-clicking the Julia executable or running
`julia` from the command line: -->
```
$ julia
_
_ _ _(_)_ | A fresh approach to technical computing
(_) | (_) (_) | Documentation: https://docs.julialang.org
_ _ _| |_ __ _ | Type "?help" for help.
| | | | | | |/ _` | |
| | |_| | | | (_| | | Version 0.5.0-dev+2440 (2016-02-01 02:22 UTC)
_/ |\__'_|_|_|\__'_| | Commit 2bb94d6 (11 days old master)
|__/ | x86_64-apple-darwin13.1.0
julia> 1 + 2
3
julia> ans
3
```
输入 `^D` (`Ctrl` 和 `d` 两键同
gitextract_uvq9cdvn/
├── .gitignore
├── .gitmodules
├── DocCheck.jl
├── Makefile
├── README.md
├── VERSION
├── _templates/
│ └── sidebarintro.html
├── conf.py
├── devdocs/
│ ├── cartesian.rst
│ └── index.rst
├── index.rst
├── latex.rst
├── manual/
│ ├── arrays.rst
│ ├── calling-c-and-fortran-code.rst
│ ├── complex-and-rational-numbers.md
│ ├── complex-and-rational-numbers.rst
│ ├── constructors.rst
│ ├── control-flow.rst
│ ├── conversion-and-promotion.rst
│ ├── dates.rst
│ ├── embedding.rst
│ ├── faq.rst
│ ├── functions.rst
│ ├── getting-started.md
│ ├── getting-started.rst
│ ├── index.rst
│ ├── integers-and-floating-point-numbers.md
│ ├── integers-and-floating-point-numbers.rst
│ ├── interacting-with-julia.rst
│ ├── introduction.rst
│ ├── linear-algebra.md
│ ├── linear-algebra.rst
│ ├── mathematical-operations.rst
│ ├── metaprogramming.rst
│ ├── methods.rst
│ ├── modules.rst
│ ├── networking-and-streams.rst
│ ├── noteworthy-differences.rst
│ ├── nullable-types.rst
│ ├── packages.rst
│ ├── parallel-computing.rst
│ ├── performance-tips.rst
│ ├── running-external-programs.rst
│ ├── stacktraces.md
│ ├── strings.rst
│ ├── style-guide.md
│ ├── style-guide.rst
│ ├── types.rst
│ ├── variables-and-scoping.rst
│ ├── variables.md
│ └── variables.rst
├── note/
│ ├── index.rst
│ ├── macroes.rst
│ ├── optimize.rst
│ └── uses.rst
├── requirements.txt
└── stdlib/
├── base.rst
├── collections.rst
├── constants.rst
├── file.rst
├── graphics.rst
├── index.rst
├── linalg.rst
├── pkg.rst
├── profile.rst
├── punctuation.rst
├── sort.rst
├── sparse.rst
└── test.rst
Condensed preview — 69 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (1,014K chars).
[
{
"path": ".gitignore",
"chars": 9,
"preview": "_build/\r\n"
},
{
"path": ".gitmodules",
"chars": 0,
"preview": ""
},
{
"path": "DocCheck.jl",
"chars": 7723,
"preview": "# Julia utilities for checking documentation\n\n# This file contains a number of functions for checking julia documentatio"
},
{
"path": "Makefile",
"chars": 5920,
"preview": "# Makefile for Sphinx documentation\n#\n\n# You can set these variables from the command line.\nSPHINXOPTS =\nSPHINXBUILD "
},
{
"path": "README.md",
"chars": 103,
"preview": "# Julia中文文档 0.3 版本翻译\n\n这是旧版 Julia 中文文档,新版Julia文档请查看 [JuliaZH.jl](https://github.com/JuliaCN/JuliaZH.jl)\n"
},
{
"path": "VERSION",
"chars": 17,
"preview": "0.3.0-prerelease\n"
},
{
"path": "_templates/sidebarintro.html",
"chars": 0,
"preview": ""
},
{
"path": "conf.py",
"chars": 8801,
"preview": "# -*- coding: utf-8 -*-\n#\n# Julia Language documentation build configuration file, created by\n# sphinx-quickstart on Sat"
},
{
"path": "devdocs/cartesian.rst",
"chars": 10398,
"preview": ".. module:: Base.Cartesian\n\n.. _devdocs-cartesian:\n\nBase.Cartesian\n==============\n\nThe (non-exported) Cartesian module p"
},
{
"path": "devdocs/index.rst",
"chars": 184,
"preview": ":orphan:\n\n.. _devdocs-index:\n\n####################################\n Documentation of Julia's Internals\n#################"
},
{
"path": "index.rst",
"chars": 1466,
"preview": "%%%%%%%%%%%%\n Julia 文档\n%%%%%%%%%%%%\n\n* :ref:`manual`\n* :ref:`stdlib`\n* :ref:`devdocs`\n* :ref:`note`\n\n.. _manual:\n\n######"
},
{
"path": "latex.rst",
"chars": 153,
"preview": ":orphan:\n\n%%%%%%%%%%%%%%%%%%%%%\n Julia Documentation\n%%%%%%%%%%%%%%%%%%%%%\n\n.. toctree::\n :maxdepth: 1\n\n manual/inde"
},
{
"path": "manual/arrays.rst",
"chars": 40500,
"preview": ".. _man-arrays:\n\n**********\n 多维数组\n**********\n.. **************************\n.. Multi-dimensional Arrays\n.. *************"
},
{
"path": "manual/calling-c-and-fortran-code.rst",
"chars": 17426,
"preview": ".. _man-calling-c-and-fortran-code:\n\n************************\n 调用 C 和 Fortran 代码\n************************\n\nJulia 调用 C 和 "
},
{
"path": "manual/complex-and-rational-numbers.md",
"chars": 8439,
"preview": "# 复数与分数\n\n<!-- # Complex and Rational Numbers -->\n\nJulia 语言自带预定义的表示复数与分数的类型,并且支持它们的各种[标准数学操作与基础函数](@ref)。由于也定义了复数与分数的[转换与"
},
{
"path": "manual/complex-and-rational-numbers.rst",
"chars": 5109,
"preview": ".. _man-complex-and-rational-numbers:\n\n************\n 复数和分数\n************\n\nJulia 提供复数和分数类型,并对其支持所有的 :ref:`标准数学运算 <man-math"
},
{
"path": "manual/constructors.rst",
"chars": 7090,
"preview": ".. _man-constructors:\n\n**********\n 构造函数\n**********\n\n构造函数 [#]_ 是构造新对象,即新 :ref:`man-composite-types` 实例的函数。在 Julia 中,\n类型本身"
},
{
"path": "manual/control-flow.rst",
"chars": 15631,
"preview": ".. _man-control-flow:\n\n********\n 控制流\n********\n\nJulia 提供一系列控制流:\n\n- :ref:`man-compound-expressions` : ``begin`` 和 ``(;)``"
},
{
"path": "manual/conversion-and-promotion.rst",
"chars": 6130,
"preview": ".. _man-conversion-and-promotion:\n\n********************\n 类型转换和类型提升\n********************\n\nJulia 可以将数学运算符的参数提升为同一个类型,这些参数的"
},
{
"path": "manual/dates.rst",
"chars": 10248,
"preview": ".. _man-dates:\n\n*********\n日期和时间\n*********\n\n``Dates`` 模块提供了两种关于时间的数据类型: ``Date`` 和 ``DateTime``, 精度分别为天和毫秒, 都是抽象数据类型 ``Ti"
},
{
"path": "manual/embedding.rst",
"chars": 11922,
"preview": ".. _man-embedding:\n\n.. highlight:: c\n\n**************************\n 嵌入式 Julia\n**************************\n\n.. As we have se"
},
{
"path": "manual/faq.rst",
"chars": 30185,
"preview": ".. _man-faq:\n\n**********\n 常见问题\n**********\n\n会话和 REPL\n-----------\n\n如何删除内存中的对象?\n~~~~~~~~~~~~~~~~~~~~~~\n\nJulia 没有 MATLAB 的 `"
},
{
"path": "manual/functions.rst",
"chars": 8289,
"preview": ".. _man-functions:\n\n******\n 函数\n******\n\nJulia 中的函数是将一系列参数组成的元组映设到一个返回值的对象,Julia 的函数不是纯的数学式函数,有些函数可以改变或者影响程序的全局状态。Julia 中定"
},
{
"path": "manual/getting-started.md",
"chars": 9029,
"preview": "# 起步\r\n\r\n<!-- # Getting Started -->\r\n\r\n不管是用编译好的程序,还是自己从源码编译,安装 Julia 都是一件很简单的事情。按照 [https://julialang.org/downloads/](htt"
},
{
"path": "manual/getting-started.rst",
"chars": 3969,
"preview": ".. _man-getting-started:\n\n****\n开始\n****\n\nJulia 的安装,不管是使用编译好的程序,还是自己从源代码编译,都很简单。按照 `这儿 <http://julialang.org/downloads/>`_"
},
{
"path": "manual/index.rst",
"chars": 620,
"preview": "\n.. _manual-index:\n\n############\n Julia 手册\n############\n\n.. toctree::\n :maxdepth: 1\n\n introduction\n getting-starte"
},
{
"path": "manual/integers-and-floating-point-numbers.md",
"chars": 31439,
"preview": "# 整数和浮点数\n\n<!-- # Integers and Floating-Point Numbers -->\n\n整数和浮点值是算术和计算的基础。这些数值内建的表示被称作数值原始类型(numeric primitive),且整数和浮点数在"
},
{
"path": "manual/integers-and-floating-point-numbers.rst",
"chars": 14105,
"preview": ".. _man-integers-and-floating-point-numbers:\n\n**************\n 整数和浮点数\n**************\n\n整数和浮点数是算术和计算的基础。它们都是数字文本。例如 ``1`` 是"
},
{
"path": "manual/interacting-with-julia.rst",
"chars": 11580,
"preview": ".. _man-interacting-with-julia:\n\n************************\n Interacting With Julia\n************************\n\nJulia comes "
},
{
"path": "manual/introduction.rst",
"chars": 2090,
"preview": ".. _man-introduction:\n\n******\n 简介\n******\n\n科学计算对性能一直有着最高的需求, 但现在这个领域的专家开始大量使用比较慢的动态语言来完成日常工作。我们相信有很多使用动态语言的理由, 所以我们不会舍弃这样"
},
{
"path": "manual/linear-algebra.md",
"chars": 44326,
"preview": "# 线性代数\n\n<!-- # Linear Algebra -->\n\n除了对多维数组的支持以外,Julia 还自带许多常用线性代数的运算。并且支持如\n[`trace`](@ref)(迹),[`det`](@ref)(行列式)和 [`inv`"
},
{
"path": "manual/linear-algebra.rst",
"chars": 6642,
"preview": "**********\n 线性代数 \n**********\n\n矩阵分解\n========\n\n`矩阵分解 <http://zh.wikipedia.org/zh-cn/%E7%9F%A9%E9%98%B5%E5%88%86%E8%A7%A3>`"
},
{
"path": "manual/mathematical-operations.rst",
"chars": 14295,
"preview": ".. _man-mathematical-operations:\n\n******************\n数学运算和基本函数\n******************\n\nJulia 为它所有的基础数值类型,提供了整套的基础算术和位运算,也提供了"
},
{
"path": "manual/metaprogramming.rst",
"chars": 17323,
"preview": ".. _man-metaprogramming:\n\n.. currentmodule:: Base\n\n********\n 元编程(Release 0.4.0)\n********\n在Julia语言中,对元编程的支持,是继承自Lisp语言的最强"
},
{
"path": "manual/methods.rst",
"chars": 14324,
"preview": ".. _man-methods:\n\n******\n 方法\n******\n\n:ref:`man-functions` 中说到,函数是从参数多元组映射到返回值的对象,若没有合适返回值则抛出异常。实际中常需要对不同类型的参数做同样的运算,例如对整"
},
{
"path": "manual/modules.rst",
"chars": 8508,
"preview": ".. _man-modules:\n\n******\n 模块\n******\n\n.. index:: module, baremodule, using, import, export, importall\n\nJulia 的模块是一个独立的全局变"
},
{
"path": "manual/networking-and-streams.rst",
"chars": 6324,
"preview": ".. _man-networking-and-streams:\n\n**********\n 网络和流 \n**********\n\nJulia 提供了一个丰富的接口处理终端、管道、tcp套接字等等I/O流对象。\n接口在系统层的实现是异步的,开发"
},
{
"path": "manual/noteworthy-differences.rst",
"chars": 6321,
"preview": ".. _man-noteworthy-differences:\n\n******************\n 与其它语言的区别\n******************\n\n与 MATLAB 的区别\n----------------\n\nJulia 的"
},
{
"path": "manual/nullable-types.rst",
"chars": 1494,
"preview": ".. _man-nullable-types:\n\n*******\n可空类型\n*******\n\n在很多情况下, 你可能会碰到一些可能存在也可能不存在的变量. 为了处理这种\n情况, Julia 提供了参数化的数据类型 ``Nullable{T}"
},
{
"path": "manual/packages.rst",
"chars": 30416,
"preview": ".. _man-packages:\n\n********\n 扩展包\n********\n\nJulia 内置了一个包管理系统,可以用这个系统来完成包的管理,当然,你也可以用你的操作系统自带的,或者从源码编译。\n你可以在 `<http://pkg."
},
{
"path": "manual/parallel-computing.rst",
"chars": 17568,
"preview": ".. _man-parallel-computing:\n\n**********\n 并行计算\n**********\n\nJulia 提供了一个基于消息传递的多处理器环境,能够同时在多处理器上使用独立的内存空间运行程序。\n\nJulia 的消息传递"
},
{
"path": "manual/performance-tips.rst",
"chars": 14417,
"preview": ".. _man-performance-tips:\n\n**************\n 代码性能优化\n**************\n\n以下几节将描述一些提高 Julia 代码运行速度的技巧。\n\n避免全局变量\n------------\n\n全局变"
},
{
"path": "manual/running-external-programs.rst",
"chars": 5416,
"preview": ".. _man-running-external-programs:\n\n**************\n 运行外部程序\n**************\n\nJulia 使用倒引号 ````` 来运行外部程序:\n\n::\n\n julia> `e"
},
{
"path": "manual/stacktraces.md",
"chars": 9860,
"preview": "# 栈跟踪\n\n<!-- # Stack Traces -->\n\n`栈跟踪`模块提供了一个返回信息易于理解和能够在程序编写过程中使用的简单的栈跟踪功能。\n\n<!-- The `StackTraces` module provides simp"
},
{
"path": "manual/strings.rst",
"chars": 14088,
"preview": ".. _man-strings:\n\n********\n 字符串\n********\n\nJulia 中处理 `ASCII <http://zh.wikipedia.org/zh-cn/ASCII>`_ 文本简洁高效,也可以处理 `Unicode"
},
{
"path": "manual/style-guide.md",
"chars": 22070,
"preview": "# 代码风格指南\n\n<!-- # Style Guide -->\n\n本文介绍了一些写出地道的 Julia 语言代码风格的注意事项。这些规则并不是绝对的,它们只是一些建议,从而帮助你更好地熟悉这门语言,并帮助你能在各种代码设计中有所选择。\n\n"
},
{
"path": "manual/style-guide.rst",
"chars": 9858,
"preview": ".. _man-style-guide:\n\n**********\n 代码样式\n**********\n\nThe following sections explain a few aspects of idiomatic Julia codin"
},
{
"path": "manual/types.rst",
"chars": 16973,
"preview": ".. _man-types:\n\n******\n 类型\n******\n\nJulia 中,如果类型被省略,则值可以是任意类型。添加类型会显著提高性能和系统稳定性。\n\nJulia `类型系统 <http://zh.wikipedia.org/zh"
},
{
"path": "manual/variables-and-scoping.rst",
"chars": 4079,
"preview": ".. _man-variables-and-scoping:\n\n**************\n 变量的作用域\n**************\n\n变量的 *作用域* 是变量可见的区域。变量作用域能帮助避免变量命名冲突。\n\n*作用域块* 是作为变"
},
{
"path": "manual/variables.md",
"chars": 6756,
"preview": "# 变量\n\n<!-- # Variables -->\n\nJulia 中,变量(Variable)即是关联(或绑定)到一个值的名字。当你想存下一个值(比如从某些数学运算后得到的)以备后用时,变量就显得非常有用。例如:\n\n<!-- A vari"
},
{
"path": "manual/variables.rst",
"chars": 2401,
"preview": "******\n 变量\n******\n\nJulia 中,变量即是关联到某个值的名字。当你想存储一个值(比如数学计算中的某个中间变量)以备后用时,变量的作用就体现出来了。举个例子:\n\n.. doctest::\n\n # 将 整数 10 赋值"
},
{
"path": "note/index.rst",
"chars": 144,
"preview": ".. _note-index:\r\n\r\n######\r\n 笔记\r\n######\r\n\r\n此笔记不是官方文档,没有任何人为此文档的时效性、准确性等负责。\r\n\r\n.. toctree::\r\n :maxdepth: 1\r\n\r\n macroes"
},
{
"path": "note/macroes.rst",
"chars": 2924,
"preview": ".. _note-macroes:\r\n\r\n****\r\n 宏\r\n****\r\n\r\n在这儿列几个推荐的宏\r\n\r\n@time\r\n-----\r\n\r\n@time 宏可打印出程序运行所需的时间。可用于单行表达式或代码块: ::\r\n\r\n\tjulia> @t"
},
{
"path": "note/optimize.rst",
"chars": 2515,
"preview": "*********\n优化代码\n*********\n\n使用宏 ``@inbounds`` 加速循环\n----------------------------\n``@inbounds`` 以牺牲程序的安全性来大幅度加快循环的执行效率。 在对数组"
},
{
"path": "note/uses.rst",
"chars": 199,
"preview": ".. _note-uses:\r\n\r\n******\r\n 心得\r\n******\r\n\r\n数字分隔符\r\n----------\r\n\r\n数字分隔符可以使数字位数更加清晰: ::\r\n\r\n julia> a = 100_000\r\n 100000"
},
{
"path": "requirements.txt",
"chars": 175,
"preview": "-e git+https://github.com/JuliaCN/JuliaDoc.git#egg=JuliaDoc\n-e git+https://github.com/snide/sphinx_rtd_theme.git@21e875d"
},
{
"path": "stdlib/base.rst",
"chars": 210467,
"preview": ".. currentmodule:: Base\n\n**********************\n The Standard Library\n**********************\n\nIntroduction\n------------\n"
},
{
"path": "stdlib/collections.rst",
"chars": 2367,
"preview": ".. module:: Base.Collections\n\nCollections and Data Structures\n===============================\n\nThe ``Collections`` modul"
},
{
"path": "stdlib/constants.rst",
"chars": 701,
"preview": "\n.. currentmodule:: Base\n\nConstants\n---------\n\n.. data:: OS_NAME\n\n A symbol representing the name of the operating sys"
},
{
"path": "stdlib/file.rst",
"chars": 4126,
"preview": "\n.. currentmodule:: Base\n\nFilesystem\n----------\n\n.. function:: isblockdev(path) -> Bool\n\n Returns ``true`` if ``path``"
},
{
"path": "stdlib/graphics.rst",
"chars": 2596,
"preview": ".. _stdlib-graphics:\n\n.. module:: Base.Graphics\n\nGraphics\n----------\n\nThe ``Base.Graphics`` interface is an abstract wra"
},
{
"path": "stdlib/index.rst",
"chars": 262,
"preview": "\n.. _stdlib-index:\n\n############################\n The Julia Standard Library\n############################\n\n.. toctree::\n"
},
{
"path": "stdlib/linalg.rst",
"chars": 41452,
"preview": ".. _stdlib-linalg:\n\nLinear Algebra\n--------------\n\n.. module:: Base.LinAlg\n\n.. currentmodule:: Base\n\nLinear algebra func"
},
{
"path": "stdlib/pkg.rst",
"chars": 6438,
"preview": ".. module:: Base.Pkg\n\nPackage Manager Functions\n-------------------------\n\nAll package manager functions are defined in "
},
{
"path": "stdlib/profile.rst",
"chars": 16694,
"preview": ".. module:: Base.Profile\n\n.. _stdlib-profiling:\n\nProfiling\n=========\n\nThe ``Profile`` module provides tools to help deve"
},
{
"path": "stdlib/punctuation.rst",
"chars": 2084,
"preview": "Punctuation\n-----------\n\nExtended documentation for mathematical symbols & functions is :ref:`here <mathematical-operato"
},
{
"path": "stdlib/sort.rst",
"chars": 7439,
"preview": "\n.. currentmodule:: Base\n\nSorting and Related Functions\n=============================\n\nJulia has an extensive, flexible "
},
{
"path": "stdlib/sparse.rst",
"chars": 4208,
"preview": ".. _stdlib-sparse:\n\n.. currentmodule:: Base\n\nSparse Matrices\n---------------\n\nSparse matrices support much of the same s"
},
{
"path": "stdlib/test.rst",
"chars": 4676,
"preview": ".. module:: Base.Test\n\nUnit and Functional Testing\n===========================\n\nThe ``Test`` module contains macros and "
}
]
About this extraction
This page contains the full source code of the JuliaCN/julia_zh_cn GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 69 files (831.5 KB), approximately 267.4k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.