├── .gitignore ├── GolangStudy.xcodeproj ├── project.pbxproj └── project.xcworkspace │ └── contents.xcworkspacedata ├── GolangStudy ├── AppDelegate.swift ├── Base.lproj │ ├── LaunchScreen.xib │ └── Main.storyboard ├── Course.swift ├── DetailViewController.swift ├── Images.xcassets │ ├── AppIcon.appiconset │ │ ├── Contents.json │ │ ├── Icon-29-1.png │ │ ├── Icon-29.png │ │ ├── Icon-29@2x-1.png │ │ ├── Icon-29@2x.png │ │ ├── Icon-40.png │ │ ├── Icon-40@2x-1.png │ │ ├── Icon-40@2x.png │ │ ├── Icon-50.png │ │ ├── Icon-50@2x.png │ │ ├── Icon-57.png │ │ ├── Icon-57@2x.png │ │ ├── Icon-60@2x.png │ │ ├── Icon-72.png │ │ ├── Icon-72@2x.png │ │ ├── Icon-76.png │ │ └── Icon-76@2x.png │ └── LaunchImage.launchimage │ │ ├── Contents.json │ │ ├── lanuch@1x.png │ │ ├── lanuch@2x-1.png │ │ ├── lanuch@2x.png │ │ ├── launch@r4-1.png │ │ └── launch@r4.png ├── Info.plist ├── ViewController.swift ├── XmlParseUtil.swift ├── basic_course_list.xml ├── example_course_list.xml └── tutorial │ ├── go_by_example │ ├── default.css │ ├── go_array.html │ ├── go_atomic_counter.html │ ├── go_base64_encoding.html │ ├── go_buffered_channel.html │ ├── go_channel_close.html │ ├── go_channel_direction.html │ ├── go_channel_range.html │ ├── go_channel_select.html │ ├── go_channel_sync.html │ ├── go_cmd_line_argument.html │ ├── go_cmd_line_tag.html │ ├── go_collection_manipulate.html │ ├── go_constant.html │ ├── go_customized_sort.html │ ├── go_defer.html │ ├── go_environment_variable.html │ ├── go_error_handle.html │ ├── go_exit.html │ ├── go_for_loop.html │ ├── go_func_callback.html │ ├── go_func_closure.html │ ├── go_func_define.html │ ├── go_func_multiple_ret_value.html │ ├── go_func_named_ret_value.html │ ├── go_hello_world.html │ ├── go_if_elseif.html │ ├── go_interface.html │ ├── go_json.html │ ├── go_line_filters.html │ ├── go_map.html │ ├── go_method.html │ ├── go_mutex.html │ ├── go_non_blocking_channel.html │ ├── go_number.html │ ├── go_number_parse.html │ ├── go_panic.html │ ├── go_parallel.html │ ├── go_parallel_channel.html │ ├── go_pointer.html │ ├── go_process_run.html │ ├── go_process_trigger.html │ ├── go_rand_number.html │ ├── go_range.html │ ├── go_read_file.html │ ├── go_recursion.html │ ├── go_regex.html │ ├── go_req_freq_control.html │ ├── go_sha1_hash.html │ ├── go_signal_handle.html │ ├── go_slice.html │ ├── go_sort.html │ ├── go_stateful_goroutine.html │ ├── go_string_byte_convert.html │ ├── go_string_format.html │ ├── go_string_manipulate.html │ ├── go_struct.html │ ├── go_switch.html │ ├── go_ticker.html │ ├── go_time.html │ ├── go_time_format_parse.html │ ├── go_timeout.html │ ├── go_timer.html │ ├── go_timestamp.html │ ├── go_url_parse.html │ ├── go_variable.html │ ├── go_variable_argument_list.html │ ├── go_working_pool.html │ ├── go_write_file.html │ └── highlight.pack.js │ └── go_tutorial │ ├── default.css │ ├── go_tutorial_10_use_package_test.html │ ├── go_tutorial_1_how_to_install_go.html │ ├── go_tutorial_2_data_type.html │ ├── go_tutorial_3_variable.html │ ├── go_tutorial_4_control_structure.html │ ├── go_tutorial_5_array_slice_map.html │ ├── go_tutorial_6_func.html │ ├── go_tutorial_7_pointer.html │ ├── go_tutorial_8_struct_interface.html │ ├── go_tutorial_9_parallel_compute.html │ └── highlight.pack.js ├── LICENSE ├── README.md └── Screenshots ├── 27D2524682F39141366C816BF4E6D9F3.png ├── 3BA9A25E9C3A79D65C23DB5ED42BEC35.png ├── 92A390D999D952485BE806130899F09B.png └── golang.gif /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | build/ 4 | *.pbxuser 5 | !default.pbxuser 6 | *.mode1v3 7 | !default.mode1v3 8 | *.mode2v3 9 | !default.mode2v3 10 | *.perspectivev3 11 | !default.perspectivev3 12 | xcuserdata 13 | *.xccheckout 14 | *.moved-aside 15 | DerivedData 16 | *.hmap 17 | *.ipa 18 | *.xcuserstate 19 | 20 | # CocoaPods 21 | # 22 | # We recommend against adding the Pods directory to your .gitignore. However 23 | # you should judge for yourself, the pros and cons are mentioned at: 24 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control 25 | # 26 | # Pods/ 27 | -------------------------------------------------------------------------------- /GolangStudy.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /GolangStudy/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // GolangStudy 4 | // 5 | // Created by scott on 14-10-25. 6 | // Copyright (c) 2014年 scott. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { 18 | // Override point for customization after application launch. 19 | return true 20 | } 21 | 22 | func applicationWillResignActive(application: UIApplication) { 23 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 24 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 25 | } 26 | 27 | func applicationDidEnterBackground(application: UIApplication) { 28 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 30 | } 31 | 32 | func applicationWillEnterForeground(application: UIApplication) { 33 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 34 | } 35 | 36 | func applicationDidBecomeActive(application: UIApplication) { 37 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 38 | } 39 | 40 | func applicationWillTerminate(application: UIApplication) { 41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 42 | } 43 | 44 | 45 | } 46 | 47 | -------------------------------------------------------------------------------- /GolangStudy/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 21 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /GolangStudy/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 35 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | -------------------------------------------------------------------------------- /GolangStudy/Course.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Course.swift 3 | // GolangStudy 4 | // 5 | // Created by scott on 14-10-26. 6 | // Copyright (c) 2014年 scott. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class Course { 12 | 13 | private var name:String? 14 | private var desc:String? 15 | private var chapter:String? 16 | 17 | func getName()->String{ 18 | return self.name! 19 | } 20 | 21 | func getDesc()->String{ 22 | return self.desc! 23 | } 24 | 25 | func getChapter()->String{ 26 | return self.chapter! 27 | } 28 | 29 | init(name:String,desc:String,chapter:String){ 30 | self.name = name 31 | self.desc = desc 32 | self.chapter = chapter 33 | } 34 | 35 | } -------------------------------------------------------------------------------- /GolangStudy/DetailViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DetailWebViewController.swift 3 | // GolangStudy 4 | // 5 | // Created by scott on 14-10-26. 6 | // Copyright (c) 2014年 scott. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class DetailViewController: UIViewController,UIWebViewDelegate{ 12 | 13 | @IBOutlet weak var webView: UIWebView! 14 | 15 | @IBOutlet weak var nextBtn: UIBarButtonItem! 16 | 17 | var courseSet:[Course] = [] 18 | var pathname:String! 19 | var index:Int = 0 20 | 21 | override func viewDidLoad() { 22 | super.viewDidLoad() 23 | webView.delegate = self 24 | loadData(pathname) 25 | } 26 | 27 | func loadData(pathname:String){ 28 | let path = NSBundle.mainBundle().pathForResource(pathname, ofType: "html")! 29 | let requestURL = NSURL(fileURLWithPath: path)! 30 | let request = NSURLRequest(URL: requestURL) 31 | webView.loadRequest(request) 32 | } 33 | 34 | func webViewDidStartLoad(webView: UIWebView) { 35 | UIApplication.sharedApplication().networkActivityIndicatorVisible = true 36 | } 37 | 38 | func webViewDidFinishLoad(webView: UIWebView) { 39 | UIApplication.sharedApplication().networkActivityIndicatorVisible = false 40 | } 41 | 42 | 43 | @IBAction func nextBtn(sender: AnyObject) { 44 | index = index+1 45 | if index < courseSet.count { 46 | var course = courseSet[index] as Course 47 | self.title = course.getName() 48 | pathname = course.getChapter() 49 | loadData(pathname) 50 | }else{ 51 | nextBtn.enabled = false 52 | } 53 | } 54 | 55 | 56 | 57 | override func didReceiveMemoryWarning() { 58 | super.didReceiveMemoryWarning() 59 | } 60 | 61 | 62 | /* 63 | // MARK: - Navigation 64 | 65 | // In a storyboard-based application, you will often want to do a little preparation before navigation 66 | override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { 67 | // Get the new view controller using segue.destinationViewController. 68 | // Pass the selected object to the new view controller. 69 | } 70 | */ 71 | 72 | } 73 | -------------------------------------------------------------------------------- /GolangStudy/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "29x29", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-29.png", 7 | "scale" : "1x" 8 | }, 9 | { 10 | "size" : "29x29", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-29@2x.png", 13 | "scale" : "2x" 14 | }, 15 | { 16 | "size" : "40x40", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-40@2x.png", 19 | "scale" : "2x" 20 | }, 21 | { 22 | "size" : "57x57", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-57.png", 25 | "scale" : "1x" 26 | }, 27 | { 28 | "size" : "57x57", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-57@2x.png", 31 | "scale" : "2x" 32 | }, 33 | { 34 | "size" : "60x60", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-60@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "idiom" : "iphone", 41 | "size" : "60x60", 42 | "scale" : "3x" 43 | }, 44 | { 45 | "size" : "29x29", 46 | "idiom" : "ipad", 47 | "filename" : "Icon-29-1.png", 48 | "scale" : "1x" 49 | }, 50 | { 51 | "size" : "29x29", 52 | "idiom" : "ipad", 53 | "filename" : "Icon-29@2x-1.png", 54 | "scale" : "2x" 55 | }, 56 | { 57 | "size" : "40x40", 58 | "idiom" : "ipad", 59 | "filename" : "Icon-40.png", 60 | "scale" : "1x" 61 | }, 62 | { 63 | "size" : "40x40", 64 | "idiom" : "ipad", 65 | "filename" : "Icon-40@2x-1.png", 66 | "scale" : "2x" 67 | }, 68 | { 69 | "size" : "50x50", 70 | "idiom" : "ipad", 71 | "filename" : "Icon-50.png", 72 | "scale" : "1x" 73 | }, 74 | { 75 | "size" : "50x50", 76 | "idiom" : "ipad", 77 | "filename" : "Icon-50@2x.png", 78 | "scale" : "2x" 79 | }, 80 | { 81 | "size" : "72x72", 82 | "idiom" : "ipad", 83 | "filename" : "Icon-72.png", 84 | "scale" : "1x" 85 | }, 86 | { 87 | "size" : "72x72", 88 | "idiom" : "ipad", 89 | "filename" : "Icon-72@2x.png", 90 | "scale" : "2x" 91 | }, 92 | { 93 | "size" : "76x76", 94 | "idiom" : "ipad", 95 | "filename" : "Icon-76.png", 96 | "scale" : "1x" 97 | }, 98 | { 99 | "size" : "76x76", 100 | "idiom" : "ipad", 101 | "filename" : "Icon-76@2x.png", 102 | "scale" : "2x" 103 | } 104 | ], 105 | "info" : { 106 | "version" : 1, 107 | "author" : "xcode" 108 | } 109 | } -------------------------------------------------------------------------------- /GolangStudy/Images.xcassets/AppIcon.appiconset/Icon-29-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/v5tech/GolangStudy/0fe20e65f8e2670bb38ba26021c7e928f5897a76/GolangStudy/Images.xcassets/AppIcon.appiconset/Icon-29-1.png -------------------------------------------------------------------------------- /GolangStudy/Images.xcassets/AppIcon.appiconset/Icon-29.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/v5tech/GolangStudy/0fe20e65f8e2670bb38ba26021c7e928f5897a76/GolangStudy/Images.xcassets/AppIcon.appiconset/Icon-29.png -------------------------------------------------------------------------------- /GolangStudy/Images.xcassets/AppIcon.appiconset/Icon-29@2x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/v5tech/GolangStudy/0fe20e65f8e2670bb38ba26021c7e928f5897a76/GolangStudy/Images.xcassets/AppIcon.appiconset/Icon-29@2x-1.png -------------------------------------------------------------------------------- /GolangStudy/Images.xcassets/AppIcon.appiconset/Icon-29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/v5tech/GolangStudy/0fe20e65f8e2670bb38ba26021c7e928f5897a76/GolangStudy/Images.xcassets/AppIcon.appiconset/Icon-29@2x.png -------------------------------------------------------------------------------- /GolangStudy/Images.xcassets/AppIcon.appiconset/Icon-40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/v5tech/GolangStudy/0fe20e65f8e2670bb38ba26021c7e928f5897a76/GolangStudy/Images.xcassets/AppIcon.appiconset/Icon-40.png -------------------------------------------------------------------------------- /GolangStudy/Images.xcassets/AppIcon.appiconset/Icon-40@2x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/v5tech/GolangStudy/0fe20e65f8e2670bb38ba26021c7e928f5897a76/GolangStudy/Images.xcassets/AppIcon.appiconset/Icon-40@2x-1.png -------------------------------------------------------------------------------- /GolangStudy/Images.xcassets/AppIcon.appiconset/Icon-40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/v5tech/GolangStudy/0fe20e65f8e2670bb38ba26021c7e928f5897a76/GolangStudy/Images.xcassets/AppIcon.appiconset/Icon-40@2x.png -------------------------------------------------------------------------------- /GolangStudy/Images.xcassets/AppIcon.appiconset/Icon-50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/v5tech/GolangStudy/0fe20e65f8e2670bb38ba26021c7e928f5897a76/GolangStudy/Images.xcassets/AppIcon.appiconset/Icon-50.png -------------------------------------------------------------------------------- /GolangStudy/Images.xcassets/AppIcon.appiconset/Icon-50@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/v5tech/GolangStudy/0fe20e65f8e2670bb38ba26021c7e928f5897a76/GolangStudy/Images.xcassets/AppIcon.appiconset/Icon-50@2x.png -------------------------------------------------------------------------------- /GolangStudy/Images.xcassets/AppIcon.appiconset/Icon-57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/v5tech/GolangStudy/0fe20e65f8e2670bb38ba26021c7e928f5897a76/GolangStudy/Images.xcassets/AppIcon.appiconset/Icon-57.png -------------------------------------------------------------------------------- /GolangStudy/Images.xcassets/AppIcon.appiconset/Icon-57@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/v5tech/GolangStudy/0fe20e65f8e2670bb38ba26021c7e928f5897a76/GolangStudy/Images.xcassets/AppIcon.appiconset/Icon-57@2x.png -------------------------------------------------------------------------------- /GolangStudy/Images.xcassets/AppIcon.appiconset/Icon-60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/v5tech/GolangStudy/0fe20e65f8e2670bb38ba26021c7e928f5897a76/GolangStudy/Images.xcassets/AppIcon.appiconset/Icon-60@2x.png -------------------------------------------------------------------------------- /GolangStudy/Images.xcassets/AppIcon.appiconset/Icon-72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/v5tech/GolangStudy/0fe20e65f8e2670bb38ba26021c7e928f5897a76/GolangStudy/Images.xcassets/AppIcon.appiconset/Icon-72.png -------------------------------------------------------------------------------- /GolangStudy/Images.xcassets/AppIcon.appiconset/Icon-72@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/v5tech/GolangStudy/0fe20e65f8e2670bb38ba26021c7e928f5897a76/GolangStudy/Images.xcassets/AppIcon.appiconset/Icon-72@2x.png -------------------------------------------------------------------------------- /GolangStudy/Images.xcassets/AppIcon.appiconset/Icon-76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/v5tech/GolangStudy/0fe20e65f8e2670bb38ba26021c7e928f5897a76/GolangStudy/Images.xcassets/AppIcon.appiconset/Icon-76.png -------------------------------------------------------------------------------- /GolangStudy/Images.xcassets/AppIcon.appiconset/Icon-76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/v5tech/GolangStudy/0fe20e65f8e2670bb38ba26021c7e928f5897a76/GolangStudy/Images.xcassets/AppIcon.appiconset/Icon-76@2x.png -------------------------------------------------------------------------------- /GolangStudy/Images.xcassets/LaunchImage.launchimage/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "orientation" : "portrait", 5 | "idiom" : "iphone", 6 | "extent" : "full-screen", 7 | "minimum-system-version" : "7.0", 8 | "filename" : "lanuch@2x-1.png", 9 | "scale" : "2x" 10 | }, 11 | { 12 | "extent" : "full-screen", 13 | "idiom" : "iphone", 14 | "subtype" : "retina4", 15 | "filename" : "launch@r4-1.png", 16 | "minimum-system-version" : "7.0", 17 | "orientation" : "portrait", 18 | "scale" : "2x" 19 | }, 20 | { 21 | "orientation" : "portrait", 22 | "idiom" : "iphone", 23 | "extent" : "full-screen", 24 | "filename" : "lanuch@1x.png", 25 | "scale" : "1x" 26 | }, 27 | { 28 | "orientation" : "portrait", 29 | "idiom" : "iphone", 30 | "extent" : "full-screen", 31 | "filename" : "lanuch@2x.png", 32 | "scale" : "2x" 33 | }, 34 | { 35 | "orientation" : "portrait", 36 | "idiom" : "iphone", 37 | "extent" : "full-screen", 38 | "filename" : "launch@r4.png", 39 | "subtype" : "retina4", 40 | "scale" : "2x" 41 | } 42 | ], 43 | "info" : { 44 | "version" : 1, 45 | "author" : "xcode" 46 | } 47 | } -------------------------------------------------------------------------------- /GolangStudy/Images.xcassets/LaunchImage.launchimage/lanuch@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/v5tech/GolangStudy/0fe20e65f8e2670bb38ba26021c7e928f5897a76/GolangStudy/Images.xcassets/LaunchImage.launchimage/lanuch@1x.png -------------------------------------------------------------------------------- /GolangStudy/Images.xcassets/LaunchImage.launchimage/lanuch@2x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/v5tech/GolangStudy/0fe20e65f8e2670bb38ba26021c7e928f5897a76/GolangStudy/Images.xcassets/LaunchImage.launchimage/lanuch@2x-1.png -------------------------------------------------------------------------------- /GolangStudy/Images.xcassets/LaunchImage.launchimage/lanuch@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/v5tech/GolangStudy/0fe20e65f8e2670bb38ba26021c7e928f5897a76/GolangStudy/Images.xcassets/LaunchImage.launchimage/lanuch@2x.png -------------------------------------------------------------------------------- /GolangStudy/Images.xcassets/LaunchImage.launchimage/launch@r4-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/v5tech/GolangStudy/0fe20e65f8e2670bb38ba26021c7e928f5897a76/GolangStudy/Images.xcassets/LaunchImage.launchimage/launch@r4-1.png -------------------------------------------------------------------------------- /GolangStudy/Images.xcassets/LaunchImage.launchimage/launch@r4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/v5tech/GolangStudy/0fe20e65f8e2670bb38ba26021c7e928f5897a76/GolangStudy/Images.xcassets/LaunchImage.launchimage/launch@r4.png -------------------------------------------------------------------------------- /GolangStudy/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | net.aimeizi.$(PRODUCT_NAME:rfc1034identifier) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | Go轻松学 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | UISupportedInterfaceOrientations~ipad 40 | 41 | UIInterfaceOrientationPortrait 42 | UIInterfaceOrientationPortraitUpsideDown 43 | UIInterfaceOrientationLandscapeLeft 44 | UIInterfaceOrientationLandscapeRight 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /GolangStudy/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // GolangStudy 4 | // 5 | // Created by scott on 14-10-25. 6 | // Copyright (c) 2014年 scott. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ViewController: UIViewController,UITableViewDataSource,UITableViewDelegate { 12 | 13 | @IBOutlet weak var tableView: UITableView! 14 | 15 | var courseSet:[Course] = [] 16 | 17 | override func viewDidLoad() { 18 | super.viewDidLoad() 19 | courseSet = XmlParseUtil(name: "basic_course_list").courseSet 20 | self.navigationItem.backBarButtonItem = UIBarButtonItem(title: "返回", style: .Plain, target: nil, action: nil) 21 | } 22 | 23 | @IBAction func segmentChanged(sender: UISegmentedControl) { 24 | if sender.selectedSegmentIndex == 0 { 25 | courseSet = XmlParseUtil(name: "basic_course_list").courseSet 26 | }else if sender.selectedSegmentIndex == 1 { 27 | courseSet = XmlParseUtil(name: "example_course_list").courseSet 28 | } 29 | tableView.reloadData() 30 | } 31 | 32 | func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 33 | return courseSet.count; 34 | } 35 | 36 | func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { 37 | var cell:UITableViewCell = tableView.dequeueReusableCellWithIdentifier("Cell")! as UITableViewCell 38 | var course:Course = courseSet[indexPath.row] as Course 39 | cell.textLabel.text = course.getName() 40 | cell.detailTextLabel?.text = course.getDesc() 41 | return cell 42 | } 43 | 44 | override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { 45 | if segue.identifier == "show" { 46 | let viewController = segue.destinationViewController as DetailViewController 47 | var course:Course = courseSet[tableView.indexPathForSelectedRow()!.row] as Course 48 | viewController.pathname = course.getChapter() 49 | viewController.title = course.getName() 50 | viewController.courseSet = courseSet 51 | viewController.index = tableView.indexPathForSelectedRow()!.row 52 | } 53 | } 54 | 55 | override func didReceiveMemoryWarning() { 56 | super.didReceiveMemoryWarning() 57 | // Dispose of any resources that can be recreated. 58 | } 59 | 60 | } 61 | 62 | -------------------------------------------------------------------------------- /GolangStudy/XmlParseUtil.swift: -------------------------------------------------------------------------------- 1 | // 2 | // XmlParseUtil.swift 3 | // GolangStudy 4 | // 5 | // Created by scott on 14-10-26. 6 | // Copyright (c) 2014年 scott. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class XmlParseUtil:NSObject,NSXMLParserDelegate { 12 | 13 | var course:Course? 14 | 15 | var courseSet:[Course] = [] 16 | 17 | var elementname:String = String() 18 | var name:String = String() 19 | var desc:String = String() 20 | var chapter:String = String() 21 | 22 | init(name:String){ 23 | super.init() 24 | let path = NSBundle.mainBundle().pathForResource(name, ofType: "xml")! 25 | let xmlparser:NSXMLParser = NSXMLParser(contentsOfURL: NSURL(fileURLWithPath: path)!)! 26 | xmlparser.delegate = self 27 | xmlparser.parse() 28 | } 29 | 30 | func parser(parser: NSXMLParser!, didStartElement elementName: String!, namespaceURI: String!, qualifiedName qName: String!, attributes attributeDict: [NSObject : AnyObject]!) { 31 | elementname = elementName 32 | if elementName == "Content"{ 33 | name = String() 34 | desc = String() 35 | chapter = String() 36 | } 37 | } 38 | 39 | func parser(parser: NSXMLParser!, foundCharacters string: String!) { 40 | let data = string.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceAndNewlineCharacterSet()) 41 | if elementname == "Name"{ 42 | name += data 43 | } else if elementname == "Abstract" { 44 | desc += data 45 | } else if elementname == "Chapter" { 46 | chapter += data 47 | } 48 | } 49 | 50 | func parser(parser: NSXMLParser!, didEndElement elementName: String!, namespaceURI: String!, qualifiedName qName: String!) { 51 | if elementName == "Content"{ 52 | course = Course(name: name, desc: desc, chapter: chapter) 53 | courseSet.append(course!) 54 | } 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /GolangStudy/basic_course_list.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Go语言安装与测试 7 | 轻松友好的安装方式,多平台支持。 8 | go_tutorial_1_how_to_install_go 9 | 10 | 11 | 内置基础数据类型 12 | 认识Go提供的清晰的数据类型,很清晰,不骗你。 13 | go_tutorial_2_data_type 14 | 15 | 16 | 变量与常量定义 17 | 学语言绕不开的变量,当然Go是静态语言,变量都是有固定类型的,程序运行过程中无法改变变量类型。 18 | go_tutorial_3_variable 19 | 20 | 21 | 控制流程 22 | 很简单,只有if,for,switch三种流程,连while都没有。 23 | go_tutorial_4_control_structure 24 | 25 | 26 | 数组,切片和字典 27 | 内置高级数据类型。如果我们需要频繁使用一些功能,与其提供包支持,不如作为内置功能提供。 28 | go_tutorial_5_array_slice_map 29 | 30 | 31 | 使用函数 32 | 代码的功能要精简,那么使用函数吧! 33 | go_tutorial_6_func 34 | 35 | 36 | 清楚的指针 37 | 不要怕,Go的指针不是C的指针,不可怕,但很有用。 38 | go_tutorial_7_pointer 39 | 40 | 41 | 结构体和接口 42 | 非常Cool的结构体功能,完全和C不同,很方便;另外Go提供的接口设计独特,值得研究。 43 | go_tutorial_8_struct_interface 44 | 45 | 46 | 并行计算 47 | 让你一见钟情的超酷功能! 48 | go_tutorial_9_parallel_compute 49 | 50 | 51 | 使用包和测试 52 | 良好的项目管理支持也是一门语言的独特之处。标准的测试用例是控制项目Bug的有效手段。 53 | go_tutorial_10_use_package_test 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /GolangStudy/example_course_list.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Go Hello World 7 | 一切源自经典! 8 | go_hello_world 9 | 10 | 11 | Go 数值 12 | 丰富的数值类型。 13 | go_number 14 | 15 | 16 | Go 变量 17 | 任何编程语言都有变量定义! 18 | go_variable 19 | 20 | 21 | Go 常量 22 | 那些不变的都叫常量。 23 | go_constant 24 | 25 | 26 | Go for循环 27 | 循环就该如此简单! 28 | go_for_loop 29 | 30 | 31 | Go if else条件判断 32 | 经典条件判断必须支持! 33 | go_if_elseif 34 | 35 | 36 | Go switch语句 37 | 让选择不再复杂! 38 | go_switch 39 | 40 | 41 | Go 数组 42 | 数组是区别于切片的固定长度的元素集合。 43 | go_array 44 | 45 | 46 | Go 切片 47 | 变才是不变的,能变确实好! 48 | go_slice 49 | 50 | 51 | Go 字典 52 | 一个键值对的集合。 53 | go_map 54 | 55 | 56 | Go range函数 57 | range是遍历神器! 58 | go_range 59 | 60 | 61 | Go 函数定义 62 | 函数是必须有的功能。 63 | go_func_define 64 | 65 | 66 | Go 函数命名返回值 67 | 存在就是合理的方便的! 68 | go_func_named_ret_value 69 | 70 | 71 | Go 函数回调 72 | 这个功能很酷,当然你不一定理解! 73 | go_func_callback 74 | 75 | 76 | Go 函数多返回值 77 | 实践️️会证明支持多返回值是很方便的! 78 | go_func_multiple_ret_value 79 | 80 | 81 | Go 可变长参数列表 82 | 比较实用的功能。 83 | go_variable_argument_list 84 | 85 | 86 | Go 闭包函数 87 | 如果你看出它好在那里,它就好在那里。 88 | go_func_closure 89 | 90 | 91 | Go 递归函数 92 | 只能说是很好的功能。 93 | go_recursion 94 | 95 | 96 | Go 指针 97 | 有用却不复杂的功能。 98 | go_pointer 99 | 100 | 101 | Go 结构体 102 | 简单实用的结构体。 103 | go_struct 104 | 105 | 106 | Go 方法 107 | 穿了马甲的函数。 108 | go_method 109 | 110 | 111 | Go 接口 112 | 一样强大,不一样简单的功能。 113 | go_interface 114 | 115 | 116 | Go 错误处理 117 | 有错要改,更要预防! 118 | go_error_handle 119 | 120 | 121 | Go 并行功能 122 | 未来计算的趋势。 123 | go_parallel 124 | 125 | 126 | Go 并行通道Channel 127 | 支持并行协程的通讯。 128 | go_parallel_channel 129 | 130 | 131 | Go 通道缓冲 132 | 缓冲就是用来提升处理速度的。 133 | go_buffered_channel 134 | 135 | 136 | Go 通道的同步功能 137 | 信息需要同步,同步需要通道。 138 | go_channel_sync 139 | 140 | 141 | Go 通道方向 142 | 通道也可以设置为只写或是只读的。 143 | go_channel_direction 144 | 145 | 146 | Go 通道选择Select 147 | 哪个ready选哪个。 148 | go_channel_select 149 | 150 | 151 | Go 超时 152 | 不傻等就得超时! 153 | go_timeout 154 | 155 | 156 | Go 非阻塞通道 157 | 阻塞有助于同步,非阻塞有助于并发。 158 | go_non_blocking_channel 159 | 160 | 161 | Go 关闭通道 162 | 通道关闭了,就只出不进了。 163 | go_channel_close 164 | 165 | 166 | Go 遍历通道 167 | 又可以看到range的身影。 168 | go_channel_range 169 | 170 | 171 | Go Timer 172 | 掌握你自己的时间。 173 | go_timer 174 | 175 | 176 | Go Ticker 177 | 滴答滴答。等等执行。 178 | go_ticker 179 | 180 | 181 | Go 工作池 182 | 工作池是一个高级内容。 183 | go_working_pool 184 | 185 | 186 | Go 请求处理频率控制 187 | 双拳不敌四掌,该控制得控制。 188 | go_req_freq_control 189 | 190 | 191 | Go 原子计数器 192 | 所谓原子就是一次只能有一个协程访问。 193 | go_atomic_counter 194 | 195 | 196 | Go 互斥 197 | 不能同时拥有的就是互斥的! 198 | go_mutex 199 | 200 | 201 | Go 状态协程 202 | 专门负责管理状态的协程。 203 | go_stateful_goroutine 204 | 205 | 206 | Go 排序 207 | 排序是常用算法。 208 | go_sort 209 | 210 | 211 | Go 自定义排序 212 | 想怎么排序随你! 213 | go_customized_sort 214 | 215 | 216 | Go Panic 217 | 不希望发生或者无法预期的事情只能panic! 218 | go_panic 219 | 220 | 221 | Go Defer 222 | 推迟但是一定会发生的事情。 223 | go_defer 224 | 225 | 226 | Go 集合操作 227 | 需要自己动手实现集合的操作。 228 | go_collection_manipulate 229 | 230 | 231 | Go String与Byte切片之间的转换 232 | Go里面string是由bytes构成的。 233 | go_string_byte_convert 234 | 235 | 236 | Go 字符串操作函数 237 | 字符串操作是程序语言的基本功能。 238 | go_string_manipulate 239 | 240 | 241 | Go 字符串格式化 242 | 有格式,才有美感。 243 | go_string_format 244 | 245 | 246 | Go 正则表达式 247 | 掌握了正则表达式,你才是高手。 248 | go_regex 249 | 250 | 251 | Go JSON支持 252 | 内置JSON数据格式支持。 253 | go_json 254 | 255 | 256 | Go 时间 257 | 时间是宇宙的组成部分。 258 | go_time 259 | 260 | 261 | Go 时间戳 262 | 计算机的时间是有起点的。 263 | go_timestamp 264 | 265 | 266 | Go 时间格式化和解析 267 | 世界上不同地区的时间格式是不一样的。 268 | go_time_format_parse 269 | 270 | 271 | Go 随机数 272 | 虽然是伪随机数,但也是随机数啊。 273 | go_rand_number 274 | 275 | 276 | Go 数字解析 277 | 数字解析是必备功能。 278 | go_number_parse 279 | 280 | 281 | Go URL解析 282 | 让你充分了解URL构成的解析方式! 283 | go_url_parse 284 | 285 | 286 | Go SHA1 散列 287 | 内置SHA1散列支持! 288 | go_sha1_hash 289 | 290 | 291 | Go Base64编码 292 | 内置Base64编码支持。 293 | go_base64_encoding 294 | 295 | 296 | Go 读取文件 297 | 读取文件内容是必须会的。 298 | go_read_file 299 | 300 | 301 | Go 写入文件 302 | 写文件是必须会的。 303 | go_write_file 304 | 305 | 306 | Go Line Filters 307 | 知道Pipe么? 这个就是! 308 | go_line_filters 309 | 310 | 311 | Go 命令行参数 312 | 构建命令行程序所必须了解的。 313 | go_cmd_line_argument 314 | 315 | 316 | Go 命令行参数标记 317 | 提供默认选项值之外的选择。 318 | go_cmd_line_tag 319 | 320 | 321 | Go 环境变量 322 | 一种常见的变量配置方式。 323 | go_environment_variable 324 | 325 | 326 | Go 进程触发 327 | 调用别的进程获取它的输出。 328 | go_process_trigger 329 | 330 | 331 | Go 进程执行 332 | 放弃自己,调用别的进程来执行。 333 | go_process_run 334 | 335 | 336 | Go 信号处理 337 | 信号处理是操作系统不可或缺的一部分。 338 | go_signal_handle 339 | 340 | 341 | Go Exit 342 | 一般不需要但是还是有用的功能。 343 | go_exit 344 | 345 | 346 | 347 | -------------------------------------------------------------------------------- /GolangStudy/tutorial/go_by_example/default.css: -------------------------------------------------------------------------------- 1 | h1,h2,h3,h4,h5,h6,p,blockquote{margin:0;padding:0}body{font-family:"Helvetica Neue",Helvetica,"Hiragino Sans GB",Arial,sans-serif;font-size:14px;line-height:18px;color:#737373;background-color:white;margin:10px 13px 10px 13px}table{margin:10px 0 15px 0;border-collapse:collapse}td,th{border:1px solid #ddd;padding:3px 10px}th{padding:5px 10px}a{color:#0069d6}a:hover{color:#0050a3;text-decoration:none}a img{border:0}p{margin-bottom:9px}h1,h2,h3,h4,h5,h6{color:#404040;line-height:36px}h1{margin-bottom:18px;font-size:30px}h2{font-size:24px}h3{font-size:18px}h4{font-size:16px}h5{font-size:14px}h6{font-size:13px}hr{margin:0 0 19px;border:0;border-bottom:1px solid #ccc}blockquote{padding:13px 13px 21px 15px;margin-bottom:18px;font-family:georgia,serif;font-style:italic}blockquote:before{content:"\201C";font-size:40px;margin-left:-10px;font-family:georgia,serif;color:#eee}blockquote p{font-size:14px;font-weight:300;line-height:18px;margin-bottom:0;font-style:italic}code,pre{font-family:Monaco,Andale Mono,Courier New,monospace}code{background-color:#fee9cc;color:rgba(0,0,0,0.75);padding:1px 3px;font-size:12px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}pre{display:block;padding:5px;line-height:16px;font-size:11px;white-space:pre-wrap;word-wrap:break-word;border:1px solid #23241f;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}pre code{color:#737373;font-size:11px;padding:0}sup{font-size:.83em;vertical-align:super;line-height:0}*{-webkit-print-color-adjust:exact}@media screen and (min-width:914px){body{width:854px;margin:10px auto}}@media print{body,code,pre code,h1,h2,h3,h4,h5,h6{color:black}table,pre{page-break-inside:avoid}}pre{background:#23241f}pre code{display:block;background:#23241f;padding:0}pre .tag,pre code{color:#f8f8f2}pre .keyword,pre .function,pre .literal,pre .change,pre .winutils,pre .flow,pre .lisp .title,pre .clojure .built_in,pre .nginx .title,pre .tex .special{color:#66d9ef}pre .variable,pre .params{color:#fd9720}pre .constant{color:#66d9ef}pre .title,pre .class .title,pre .css .class{color:#a6e22e}pre .attribute,pre .symbol,pre .symbol .string,pre .tag .title,pre .value,pre .css .tag{color:#f92672}pre .number,pre .preprocessor,pre .pragma,pre .regexp{color:#ae81ff}pre .tag .value,pre .string,pre .css .id,pre .subst,pre .haskell .type,pre .ruby .class .parent,pre .built_in,pre .sql .aggregate,pre .django .template_tag,pre .django .variable,pre .smalltalk .class,pre .django .filter .argument,pre .smalltalk .localvars,pre .smalltalk .array,pre .attr_selector,pre .pseudo,pre .addition,pre .stream,pre .envvar,pre .apache .tag,pre .apache .cbracket,pre .tex .command,pre .prompt{color:#e6db74}pre .comment,pre .javadoc,pre .java .annotation,pre .python .decorator,pre .template_comment,pre .pi,pre .doctype,pre .deletion,pre .shebang,pre .apache .sqbracket,pre .tex .formula{color:#a6e22d;}pre .coffeescript .javascript,pre .javascript .xml,pre .tex .formula{opacity:.5}pre .xml .javascript,pre .xml .vbscript,pre .xml .css,pre .xml .cdata{opacity:.5} -------------------------------------------------------------------------------- /GolangStudy/tutorial/go_by_example/go_array.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Go 数组 10 | 11 | 12 |

数组是一个具有相同数据类型的元素组成的固定长度有序集合

13 |

在Go语言中,数组是值类型,长度是类型的组成部分,也就是说"[10]int"和“[20]int”是完全不同的两种数组类型。

14 |

同类型的两个数组支持"=="和"!="比较,但是不能比较大小。

15 |

数组作为参数时,函数内部不改变数组内部的值,除非是传入数组的指针。

16 |

数组的指针:*[3]int

17 |

指针数组:[2]*int

18 | 19 |

示例1:

20 |
package main
 21 | 
 22 | import "fmt"
 23 | 
 24 | func main() {
 25 | 
 26 |     // 这里我们创建了一个具有5个元素的整型数组
 27 |     // 元素的数据类型和数组长度都是数组的一部分
 28 |     // 默认情况下,数组元素都是零值
 29 |     // 对于整数,零值就是0
 30 |     var a [5]int
 31 |     fmt.Println("emp:", a)
 32 | 
 33 |     // 我们可以使用索引来设置数组元素的值,就像这样
 34 |     // "array[index] = value"  或者使用索引来获取元素值,
 35 |     // 就像这样"array[index]"
 36 |     a[4] = 100
 37 |     fmt.Println("set:", a)
 38 |     fmt.Println("get:", a[4])
 39 | 
 40 |     // 内置的len函数返回数组的长度
 41 |     fmt.Println("len:", len(a))
 42 | 
 43 |     // 这种方法可以同时定义和初始化一个数组
 44 |     b := [5]int{1, 2, 3, 4, 5}
 45 |     fmt.Println("dcl:", b)
 46 | 
 47 |     // 数组都是一维的,但是你可以把数组的元素定义为一个数组
 48 |     // 来获取多维数组结构
 49 |     var twoD [2][3]int
 50 |     for i := 0; i < 2; i++ {
 51 |         for j := 0; j < 3; j++ {
 52 |             twoD[i][j] = i + j
 53 |         }
 54 |     }
 55 |     fmt.Println("2d: ", twoD)
 56 | }
 57 | 
58 |

输出结果为

59 |
emp: [0 0 0 0 0]
 60 | set: [0 0 0 0 100]
 61 | get: 100
 62 | len: 5
 63 | dcl: [1 2 3 4 5]
 64 | 2d:  [[0 1 2] [1 2 3]]
 65 | 
66 |

拥有固定长度是数组的一个特点,但是这个特点有时候会带来很多不便,尤其在一个集合元素个数不固定的情况下。这个时候我们更多地使用切片

67 |

示例2:

68 |

可以用new创建数组,并返回数组的指针

69 |
package main
 70 | 
 71 | import "fmt"
 72 | 
 73 | func main() {
 74 |     var a = new([5]int)
 75 |     test(a)
 76 |     fmt.Println(a, len(a))
 77 | }
 78 | 
 79 | func test(a *[5]int) {
 80 |     a[1] = 5
 81 | }
 82 | 
83 |

输出结果:

84 |
&[0 5 0 0 0] 5
 85 | 
86 |

示例3:

87 |
package main
 88 | 
 89 | import "fmt"
 90 | 
 91 | func main() {
 92 |     a := [...]User{
 93 |         {0, "User0"},
 94 |         {8, "User8"},
 95 |     }
 96 |     b := [...]*User{
 97 |         {0, "User0"},
 98 |         {8, "User8"},
 99 |     }
100 |     fmt.Println(a, len(a))
101 |     fmt.Println(b, len(b))
102 | 
103 | }
104 | 
105 | type User struct {
106 |     Id   int
107 |     Name string
108 | }
109 | 
110 |

输出结果:

111 |
[{0 User0} {8 User8}] 2
112 | [0x1f216130 0x1f216140] 2
113 | 
114 | 115 | -------------------------------------------------------------------------------- /GolangStudy/tutorial/go_by_example/go_atomic_counter.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Go 原子计数器 10 | 11 | 12 |

Go里面的管理协程状态的主要机制就是通道通讯。这些我们上面的例子介绍过。这里还有一些管理状态的机制,下面我们看看多协程原子访问计数器的例子,这个功能是由sync/atomic包提供的函数来实现的。

13 |
package main
14 | 
15 | import "fmt"
16 | import "time"
17 | import "sync/atomic"
18 | import "runtime"
19 | 
20 | func main() {
21 | 
22 |     // 我们使用一个无符号整型来代表一个永远为正整数的counter
23 |     var ops uint64 = 0
24 | 
25 |     // 为了模拟并行更新,我们使用50个协程来每隔1毫秒来
26 |     // 增加一下counter值,注意这里的50协程里面的for循环,
27 |     // 也就是说如果主协程不退出,这些协程将永远运行下去
28 |     // 所以这个程序每次输出的值有可能不一样
29 |     for i := 0; i < 50; i++ {
30 |         go func() {
31 |             for {
32 |                 // 为了能够保证counter值增加的原子性,我们使用
33 |                 // atomic包中的AddUint64方法,将counter的地址和
34 |                 // 需要增加的值传递给函数即可
35 |                 atomic.AddUint64(&ops, 1)
36 | 
37 |                 // 允许其他的协程来处理
38 |                 runtime.Gosched()
39 |             }
40 |         }()
41 |     }
42 | 
43 |     //等待1秒中,让协程有时间运行一段时间
44 |     time.Sleep(time.Second)
45 | 
46 |     // 为了能够在counter仍被其他协程更新值的同时安全访问counter值,
47 |     // 我们获取一个当前counter值的拷贝,这里就是opsFinal,需要把
48 |     // ops的地址传递给函数`LoadUint64`
49 |     opsFinal := atomic.LoadUint64(&ops)
50 |     fmt.Println("ops:", opsFinal)
51 | }
52 | 
53 |

我们多运行几次,结果如下:

54 |
ops: 7499289
55 | ops: 7700843
56 | ops: 7342417
57 | 
58 | 59 | -------------------------------------------------------------------------------- /GolangStudy/tutorial/go_by_example/go_base64_encoding.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Go Base64编码 10 | 11 | 12 |

Go提供了对base64编码和解码的内置支持

13 |
package main
14 | 
15 | // 这种导入包的语法将默认的base64起了一个别名b64,这样
16 | // 我们在下面就可以直接使用b64表示这个包,省点输入量
17 | import b64 "encoding/base64"
18 | import "fmt"
19 | 
20 | func main() {
21 | 
22 |     // 这里是我们用来演示编码和解码的字符串
23 |     data := "abc123!?$*&()'-=@~"
24 | 
25 |     // Go支持标准的和兼容URL的base64编码。
26 |     // 我们这里使用标准的base64编码,这个
27 |     // 函数需要一个`[]byte`参数,所以将这
28 |     // 个字符串转换为字节数组
29 |     sEnc := b64.StdEncoding.EncodeToString([]byte(data))
30 |     fmt.Println(sEnc)
31 | 
32 |     // 解码一个base64编码可能返回一个错误,
33 |     // 如果你不知道输入是否是正确的base64
34 |     // 编码,你需要检测一些解码错误
35 |     sDec, _ := b64.StdEncoding.DecodeString(sEnc)
36 |     fmt.Println(string(sDec))
37 |     fmt.Println()
38 | 
39 |     // 使用兼容URL的base64编码和解码
40 |     uEnc := b64.URLEncoding.EncodeToString([]byte(data))
41 |     fmt.Println(uEnc)
42 |     uDec, _ := b64.URLEncoding.DecodeString(uEnc)
43 |     fmt.Println(string(uDec))
44 | }
45 | 
46 |

运行结果

47 |
YWJjMTIzIT8kKiYoKSctPUB+
48 | abc123!?$*&()'-=@~
49 | 
50 | YWJjMTIzIT8kKiYoKSctPUB-
51 | abc123!?$*&()'-=@~
52 | 
53 |

这两种方法都将原数据编码为base64编码,区别在于标准的编码后面是+,而兼容URL的编码方式后面是-

54 | 55 | -------------------------------------------------------------------------------- /GolangStudy/tutorial/go_by_example/go_buffered_channel.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Go 通道缓冲 10 | 11 | 12 |

默认情况下,通道是不带缓冲区的。 13 | 发送端发送数据,同时必须又接收端相应的接收数据。 14 | 而带缓冲区的通道则允许发送端的数据发送和接收端的数据获取处于异步状态,就是说发送端发送的数据可以放在缓冲区里面,可以等待接收端去获取数据,而不是立刻需要接收端去获取数据。 15 | 不过由于缓冲区的大小是有限的,所以还是必须有接收端来接收数据的,否则缓冲区一满,数据发送端就无法再发送数据了。

16 |
package main
17 | 
18 | import "fmt"
19 | 
20 | func main() {
21 | 
22 |     // 这里我们定义了一个可以存储字符串类型的带缓冲通道
23 |     // 缓冲区大小为2
24 |     messages := make(chan string, 2)
25 | 
26 |     // 因为messages是带缓冲的通道,我们可以同时发送两个数据
27 |     // 而不用立刻需要去同步读取数据
28 |     messages <- "buffered"
29 |     messages <- "channel"
30 | 
31 |     // 然后我们和上面例子一样获取这两个数据
32 |     fmt.Println(<-messages)
33 |     fmt.Println(<-messages)
34 | }
35 | 
36 |

运行结果

37 |
buffered
38 | channel
39 | 
40 | 41 | -------------------------------------------------------------------------------- /GolangStudy/tutorial/go_by_example/go_channel_close.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Go 关闭通道 10 | 11 | 12 |

关闭通道的意思是该通道将不再允许写入数据。这个方法可以让通道数据的接受端知道数据已经全部发送完成了。

13 |
package main
14 | 
15 | import "fmt"
16 | 
17 | // 在这个例子中,我们使用通道jobs在main函数所在的协程和一个数据
18 | // 接收端所在的协程通信。当我们数据发送完成后,我们关闭jobs通道
19 | func main() {
20 |     jobs := make(chan int, 5)
21 |     done := make(chan bool)
22 | 
23 |     // 这里是数据接收端协程,它重复使用`j, more := <-jobs`来从通道
24 |     // jobs获取数据,这里的more在通道关闭且通道中不再有数据可以接收的
25 |     // 时候为false,我们通过判断more来决定所有的数据是否已经接收完成。
26 |     // 如果所有数据接收完成,那么向done通道写入true
27 |     go func() {
28 |         for {
29 |             j, more := <-jobs
30 |             if more {
31 |                 fmt.Println("received job", j)
32 |             } else {
33 |                 fmt.Println("received all jobs")
34 |                 done <- true
35 |                 return
36 |             }
37 |         }
38 |     }()
39 | 
40 |     // 这里向jobs通道写入三个数据,然后关闭通道
41 |     for j := 1; j <= 3; j++ {
42 |         jobs <- j
43 |         fmt.Println("sent job", j)
44 |     }
45 |     close(jobs)
46 |     fmt.Println("sent all jobs")
47 | 
48 |     // 我们知道done通道在接收数据的时候会阻塞,所以在所有的数据发送
49 |     // 接收完成后,写入done的数据将在这里被接收,然后程序结束。
50 |     <-done
51 | }
52 | 
53 |

运行结果

54 |
sent job 1
55 | received job 1
56 | sent job 2
57 | sent job 3
58 | sent all jobs
59 | received job 2
60 | received job 3
61 | received all jobs
62 | 
63 | 64 | -------------------------------------------------------------------------------- /GolangStudy/tutorial/go_by_example/go_channel_direction.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Go 通道方向 10 | 11 | 12 |

当使用通道作为函数的参数时,你可以指定该通道是只读的还是只写的。这种设置有时候会提高程序的参数类型安全。

13 |
package main
14 | 
15 | import "fmt"
16 | 
17 | // 这个ping函数只接收能够发送数据的通道作为参数,试图从这个通道接收数据
18 | // 会导致编译错误,这里只写的定义方式为`chan<- string`表示这个类型为
19 | // 字符串的通道为只写通道
20 | func ping(pings chan<- string, msg string) {
21 |     pings <- msg
22 | }
23 | 
24 | // pong函数接收两个通道参数,一个是只读的pings,使用`<-chan string`定义
25 | // 另外一个是只写的pongs,使用`chan<- string`来定义
26 | func pong(pings <-chan string, pongs chan<- string) {
27 |     msg := <-pings
28 |     pongs <- msg
29 | }
30 | 
31 | func main() {
32 |     pings := make(chan string, 1)
33 |     pongs := make(chan string, 1)
34 |     ping(pings, "passed message")
35 |     pong(pings, pongs)
36 |     fmt.Println(<-pongs)
37 | }
38 | 
39 |

运行结果

40 |
passed message
41 | 
42 |

其实这个例子就是把信息首先写入pings通道里面,然后在pong函数里面再把信息从pings通道里面读出来再写入pongs通道里面,最后在main函数里面将信息从pongs通道里面读出来。 43 | 在这里,pings和pongs事实上是可读且可写的,不过作为参数传递的时候,函数参数限定了通道的方向。不过pings和pongs在ping和pong函数里面还是可读且可写的。只是ping和pong函数调用的时候把它们当作了只读或者只写。

44 | 45 | -------------------------------------------------------------------------------- /GolangStudy/tutorial/go_by_example/go_channel_range.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Go 遍历通道 10 | 11 | 12 |

我们知道range函数可以遍历数组,切片,字典等。这里我们还可以使用range函数来遍历通道以接收通道数据。

13 |
package main
14 | 
15 | import "fmt"
16 | 
17 | func main() {
18 | 
19 |     // 我们遍历queue通道里面的两个数据
20 |     queue := make(chan string, 2)
21 |     queue <- "one"
22 |     queue <- "two"
23 |     close(queue)
24 | 
25 |     // range函数遍历每个从通道接收到的数据,因为queue再发送完两个
26 |     // 数据之后就关闭了通道,所以这里我们range函数在接收到两个数据
27 |     // 之后就结束了。如果上面的queue通道不关闭,那么range函数就不
28 |     // 会结束,从而在接收第三个数据的时候就阻塞了。
29 |     for elem := range queue {
30 |         fmt.Println(elem)
31 |     }
32 | }
33 | 
34 |

运行结果

35 |
one
36 | two
37 | 
38 |

这个例子同时说明了,即使关闭了一个非空通道,我们仍然可以从通道里面接收到值。

39 | 40 | -------------------------------------------------------------------------------- /GolangStudy/tutorial/go_by_example/go_channel_select.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Go 通道选择Select 10 | 11 | 12 |

Go的select关键字可以让你同时等待多个通道操作,将协程(goroutine),通道(channel)和select结合起来构成了Go的一个强大特性。

13 |
package main
14 | 
15 | import "time"
16 | import "fmt"
17 | 
18 | func main() {
19 | 
20 |     // 本例中,我们从两个通道中选择
21 |     c1 := make(chan string)
22 |     c2 := make(chan string)
23 | 
24 |     // 为了模拟并行协程的阻塞操作,我们让每个通道在一段时间后再写入一个值
25 |     go func() {
26 |         time.Sleep(time.Second * 1)
27 |         c1 <- "one"
28 |     }()
29 |     go func() {
30 |         time.Sleep(time.Second * 2)
31 |         c2 <- "two"
32 |     }()
33 | 
34 |     // 我们使用select来等待这两个通道的值,然后输出
35 |     for i := 0; i < 2; i++ {
36 |         select {
37 |         case msg1 := <-c1:
38 |             fmt.Println("received", msg1)
39 |         case msg2 := <-c2:
40 |             fmt.Println("received", msg2)
41 |         }
42 |     }
43 | }
44 | 
45 |

输出结果

46 |
received one
47 | received two
48 | 
49 |

如我们所期望的,程序输出了正确的值。对于select语句而言,它不断地检测通道是否有值过来,一旦发现有值过来,立刻获取输出。

50 | 51 | -------------------------------------------------------------------------------- /GolangStudy/tutorial/go_by_example/go_channel_sync.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Go 通道的同步功能 10 | 11 | 12 |

我们使用通道来同步协程之间的执行。 13 | 下面的例子是通过获取同步通道数据来阻塞程序执行的方法来等待另一个协程运行结束的。 14 | 也就是说main函数所在的协程在运行到<-done语句的时候将一直等待worker函数所在的协程执行完成,向通道写入数据才会(从通道获得数据)继续执行。

15 |
package main
16 | 
17 | import "fmt"
18 | import "time"
19 | 
20 | // 这个worker函数将以协程的方式运行
21 | // 通道`done`被用来通知另外一个协程这个worker函数已经执行完成
22 | func worker(done chan bool) {
23 |     fmt.Print("working...")
24 |     time.Sleep(time.Second)
25 |     fmt.Println("done")
26 | 
27 |     // 向通道发送一个数据,表示worker函数已经执行完成
28 |     done <- true
29 | }
30 | 
31 | func main() {
32 | 
33 |     // 使用协程来调用worker函数,同时将通道`done`传递给协程
34 |     // 以使得协程可以通知别的协程自己已经执行完成
35 |     done := make(chan bool, 1)
36 |     go worker(done)
37 | 
38 |     // 一直阻塞,直到从worker所在协程获得一个worker执行完成的数据
39 |     <-done
40 | }
41 | 
42 |

运行结果

43 |
working...done
44 | 
45 |

如果我们从main函数里面移除<-done语句,那么main函数在worker协程开始运行之前就结束了。

46 | 47 | -------------------------------------------------------------------------------- /GolangStudy/tutorial/go_by_example/go_cmd_line_argument.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Go 命令行参数 10 | 11 | 12 |

命令行参数是一种指定程序运行初始参数的常用方式。比如go run hello.go使用runhello.go参数来执行程序。

13 |
package main
14 | 
15 | import "os"
16 | import "fmt"
17 | 
18 | func main() {
19 | 
20 |     // `os.Args`提供了对命令行参数的访问,注意该
21 |     // 切片的第一个元素是该程序的运行路径,而
22 |     // `os.Args[1:]`则包含了该程序的所有参数
23 |     argsWithProg := os.Args
24 |     argsWithoutProg := os.Args[1:]
25 | 
26 |     // 你可以使用索引的方式来获取单个参数
27 |     arg := os.Args[3]
28 | 
29 |     fmt.Println(argsWithProg)
30 |     fmt.Println(argsWithoutProg)
31 |     fmt.Println(arg)
32 | }
33 | 
34 |

在运行该程序的时候,需要首先用go build将代码编译为可执行文件,然后提供足够数量的参数。例如

35 |
$ go build command-line-arguments.go
36 | $ ./command-line-arguments a b c d
37 | [./command-line-arguments a b c d]       
38 | [a b c d]
39 | c
40 | 
41 | 42 | -------------------------------------------------------------------------------- /GolangStudy/tutorial/go_by_example/go_cmd_line_tag.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Go 命令行参数标记 10 | 11 | 12 |

命令行参数标记是为命令行程序指定选项参数的常用方法。例如,在命令wc -l中,-l就是一个命令行参数标记。

13 |

Go提供了flag包来支持基本的命令行标记解析。我们这里将要使用这个包提供的方法来实现带选项的命令行程序。

14 |
package main
 15 | 
 16 | import "flag"
 17 | import "fmt"
 18 | 
 19 | func main() {
 20 | 
 21 |     // 基础的标记声明适用于string,integer和bool型选项。
 22 |     // 这里我们定义了一个标记`word`,默认值为`foo`和一
 23 |     // 个简短的描述。`flag.String`函数返回一个字符串指
 24 |     // 针(而不是一个字符串值),我们下面将演示如何使
 25 |     // 用这个指针
 26 |     wordPtr := flag.String("word", "foo", "a string")
 27 | 
 28 |     // 这里定义了两个标记,一个`numb`,另一个是`fork`,
 29 |     // 使用和上面定义`word`标记相似的方法
 30 |     numbPtr := flag.Int("numb", 42, "an int")
 31 |     boolPtr := flag.Bool("fork", false, "a bool")
 32 | 
 33 |     // 你也可以程序中任意地方定义的变量来定义选项,只
 34 |     // 需要把该变量的地址传递给flag声明函数即可
 35 |     var svar string
 36 |     flag.StringVar(&svar, "svar", "bar", "a string var")
 37 | 
 38 |     // 当所有的flag声明完成后,使用`flag.Parse()`来分
 39 |     // 解命令行选项
 40 |     flag.Parse()
 41 | 
 42 |     // 这里我们仅仅输出解析后的选项和任何紧跟着的位置
 43 |     // 参数,注意我们需要使用`*wordPtr`的方式来获取最
 44 |     // 后的选项值
 45 |     fmt.Println("word:", *wordPtr)
 46 |     fmt.Println("numb:", *numbPtr)
 47 |     fmt.Println("fork:", *boolPtr)
 48 |     fmt.Println("svar:", svar)
 49 |     fmt.Println("tail:", flag.Args())
 50 | }
 51 | 
52 |

为了运行示例,你需要先将程序编译为可执行文件。

53 |
go build command-line-flags.go
 54 | 
55 |

下面分别看看给予该命令行程序不同选项参数的例子:

56 |

(1) 给所有的选项设置一个参数

57 |
$ ./command-line-flags -word=opt -numb=7 -fork -svar=flag
 58 | word: opt
 59 | numb: 7
 60 | fork: true
 61 | svar: flag
 62 | tail: []
 63 | 
64 |

(2) 如果你不设置flag,那么它们自动采用默认的值

65 |
$ ./command-line-flags -word=opt
 66 | word: opt
 67 | numb: 42
 68 | fork: false
 69 | svar: bar
 70 | tail: []
 71 | 
72 |

(3) 尾部的位置参数可以出现在任意一个flag后面

73 |
$ ./command-line-flags -word=opt a1 a2 a3
 74 | word: opt
 75 | numb: 42
 76 | fork: false
 77 | svar: bar
 78 | tail: [a1 a2 a3]
 79 | 
80 |

(4) 注意flag包要求所有的flag都必须出现在尾部位置参数的前面,否则这些flag将被当作位置参数处理

81 |
$ ./command-line-flags -word=opt a1 a2 a3 -numb=7
 82 | word: opt
 83 | numb: 42
 84 | fork: false
 85 | svar: bar
 86 | trailing: [a1 a2 a3 -numb=7]
 87 | 
88 |

(5) 使用-h或者--help这两个flag来自动地生成命令行程序的帮助信息

89 |
$ ./command-line-flags -h
 90 | Usage of ./command-line-flags:
 91 |   -fork=false: a bool
 92 |   -numb=42: an int
 93 |   -svar="bar": a string var
 94 |   -word="foo": a string
 95 | 
96 |

(6) 如果你提供了一个程序不支持的flag,那么程序会打印一个错误信息和帮助信息

97 |
$ ./command-line-flags -wat
 98 | flag provided but not defined: -wat
 99 | Usage of ./go_cmd_flag:
100 |   -fork=false: a bool
101 |   -numb=42: an int
102 |   -svar="bar": a string var
103 |   -word="foo": a string
104 | 
105 | 106 | -------------------------------------------------------------------------------- /GolangStudy/tutorial/go_by_example/go_collection_manipulate.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Go 集合功能 10 | 11 | 12 |

我们经常需要程序去处理一些集合数据,比如选出所有符合条件的数据或者使用一个自定义函数将一个集合元素拷贝到另外一个集合。

13 |

在一些语言里面,通常是使用泛化数据结构或者算法。但是Go不支持泛化类型,在Go里面如果你的程序或者数据类型需要操作集合,那么通常是为集合提供一些操作函数。

14 |

这里演示了一些操作strings切片的集合函数,你可以使用这些例子来构建你自己的函数。注意在有些情况下,使用内联集合操作代码会更清晰,而不是去创建新的帮助函数。

15 |
package main
 16 | 
 17 | import "strings"
 18 | import "fmt"
 19 | 
 20 | // 返回t在vs中第一次出现的索引,如果没有找到t,返回-1
 21 | func Index(vs []string, t string) int {
 22 |     for i, v := range vs {
 23 |         if v == t {
 24 |             return i
 25 |         }
 26 |     }
 27 |     return -1
 28 | }
 29 | 
 30 | // 如果t存在于vs中,那么返回true,否则false
 31 | func Include(vs []string, t string) bool {
 32 |     return Index(vs, t) >= 0
 33 | }
 34 | 
 35 | // 如果使用vs中的任何一个字符串作为函数f的参数可以让f返回true,
 36 | // 那么返回true,否则false
 37 | func Any(vs []string, f func(string) bool) bool {
 38 |     for _, v := range vs {
 39 |         if f(v) {
 40 |             return true
 41 |         }
 42 |     }
 43 |     return false
 44 | }
 45 | 
 46 | // 如果分别使用vs中所有的字符串作为f的参数都能让f返回true,
 47 | // 那么返回true,否则返回false
 48 | func All(vs []string, f func(string) bool) bool {
 49 |     for _, v := range vs {
 50 |         if !f(v) {
 51 |             return false
 52 |         }
 53 |     }
 54 |     return true
 55 | }
 56 | 
 57 | // 返回一个新的字符串切片,切片的元素为vs中所有能够让函数f
 58 | // 返回true的元素
 59 | func Filter(vs []string, f func(string) bool) []string {
 60 |     vsf := make([]string, 0)
 61 |     for _, v := range vs {
 62 |         if f(v) {
 63 |             vsf = append(vsf, v)
 64 |         }
 65 |     }
 66 |     return vsf
 67 | }
 68 | 
 69 | // 返回一个bool类型切片,切片的元素为vs中所有字符串作为f函数
 70 | // 参数所返回的结果
 71 | func Map(vs []string, f func(string) string) []string {
 72 |     vsm := make([]string, len(vs))
 73 |     for i, v := range vs {
 74 |         vsm[i] = f(v)
 75 |     }
 76 |     return vsm
 77 | }
 78 | 
 79 | func main() {
 80 | 
 81 |     // 来,试试我们的字符串切片操作函数
 82 |     var strs = []string{"peach", "apple", "pear", "plum"}
 83 | 
 84 |     fmt.Println(Index(strs, "pear"))
 85 | 
 86 |     fmt.Println(Include(strs, "grape"))
 87 | 
 88 |     fmt.Println(Any(strs, func(v string) bool {
 89 |         return strings.HasPrefix(v, "p")
 90 |     }))
 91 | 
 92 |     fmt.Println(All(strs, func(v string) bool {
 93 |         return strings.HasPrefix(v, "p")
 94 |     }))
 95 | 
 96 |     fmt.Println(Filter(strs, func(v string) bool {
 97 |         return strings.Contains(v, "e")
 98 |     }))
 99 | 
100 |     // 上面的例子都使用匿名函数,你也可以使用命名函数
101 |     fmt.Println(Map(strs, strings.ToUpper))
102 | }
103 | 
104 |

运行结果

105 |
2
106 | false
107 | true
108 | false
109 | [peach apple pear]
110 | [PEACH APPLE PEAR PLUM]
111 | 
112 | 113 | -------------------------------------------------------------------------------- /GolangStudy/tutorial/go_by_example/go_constant.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Go 常量 10 | 11 | 12 |

Go支持定义字符常量,字符串常量,布尔型常量和数值常量。

13 |

使用const关键字来定义常量。

14 |
package main
15 | 
16 | import "fmt"
17 | import "math"
18 | 
19 | // "const" 关键字用来定义常量
20 | const s string = "constant"
21 | 
22 | func main() {
23 |     fmt.Println(s)
24 | 
25 |     // "const"关键字可以出现在任何"var"关键字出现的地方
26 |     // 区别是常量必须有初始值
27 |     const n = 500000000
28 | 
29 |     // 常量表达式可以执行任意精度数学计算
30 |     const d = 3e20 / n
31 |     fmt.Println(d)
32 | 
33 |     // 数值型常量没有具体类型,除非指定一个类型
34 |     // 比如显式类型转换
35 |     fmt.Println(int64(d))
36 | 
37 |     // 数值型常量可以在程序的逻辑上下文中获取类型
38 |     // 比如变量赋值或者函数调用。
39 |     // 例如,对于math包中的Sin函数,它需要一个float64类型的变量
40 |     fmt.Println(math.Sin(n))
41 | }
42 | 
43 |

输出结果为

44 |
constant
45 | 6e+11
46 | 600000000000
47 | -0.28470407323754404
48 | 
49 | 50 | -------------------------------------------------------------------------------- /GolangStudy/tutorial/go_by_example/go_customized_sort.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Go 自定义排序 10 | 11 | 12 |

有的时候我们希望排序不是仅仅按照自然顺序排序。例如,我们希望按照字符串的长度来对一个字符串数组排序而不是按照字母顺序来排序。这里我们介绍一下Go的自定义排序。

13 |
package main
14 | 
15 | import "sort"
16 | import "fmt"
17 | 
18 | // 为了能够使用自定义函数来排序,我们需要一个
19 | // 对应的排序类型,比如这里我们为内置的字符串
20 | // 数组定义了一个别名ByLength
21 | type ByLength []string
22 | 
23 | // 我们实现了sort接口的Len,Less和Swap方法
24 | // 这样我们就可以使用sort包的通用方法Sort
25 | // Len和Swap方法的实现在不同的类型之间大致
26 | // 都是相同的,只有Less方法包含了自定义的排序
27 | // 逻辑,这里我们希望以字符串长度升序排序
28 | func (s ByLength) Len() int {
29 |     return len(s)
30 | }
31 | func (s ByLength) Swap(i, j int) {
32 |     s[i], s[j] = s[j], s[i]
33 | }
34 | func (s ByLength) Less(i, j int) bool {
35 |     return len(s[i]) < len(s[j])
36 | }
37 | 
38 | // 一切就绪之后,我们就可以把需要进行自定义排序
39 | // 的字符串类型fruits转换为ByLength类型,然后使用
40 | // sort包的Sort方法来排序
41 | func main() {
42 |     fruits := []string{"peach", "banana", "kiwi"}
43 |     sort.Sort(ByLength(fruits))
44 |     fmt.Println(fruits)
45 | }
46 | 
47 |

输出结果

48 |
[kiwi peach banana]
49 | 
50 |

同样的,对于其他的类型,使用这种方法,我们可以为Go的切片提供任意的排序方法。归纳一下就是:

51 |
    52 |
  1. 创建自定义排序类型
  2. 53 |
  3. 实现sort包的接口方法Len,Swap和Less
  4. 54 |
  5. 使用sort.Sort方法来排序
  6. 55 |
56 | 57 | -------------------------------------------------------------------------------- /GolangStudy/tutorial/go_by_example/go_defer.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Go Defer 10 | 11 | 12 |

Defer 用来保证一个函数调用会在程序执行的最后被调用。通常用于资源清理工作。

13 |
package main
14 | 
15 | import "fmt"
16 | import "os"
17 | 
18 | // 假设我们想创建一个文件,然后写入数据,最后关闭文件
19 | func main() {
20 |     // 在使用createFile得到一个文件对象之后,我们使用defer
21 |     // 来调用关闭文件的方法closeFile,这个方法将在main函数
22 |     // 最后被执行,也就是writeFile完成之后
23 |     f := createFile("/tmp/defer.txt")
24 |     // Windows下面使用这个语句
25 |     // f := createFile("D:\\Temp\\defer.txt")
26 |     defer closeFile(f)
27 |     writeFile(f)
28 | }
29 | 
30 | func createFile(p string) *os.File {
31 |     fmt.Println("creating")
32 |     f, err := os.Create(p)
33 |     if err != nil {
34 |         panic(err)
35 |     }
36 |     return f
37 | }
38 | 
39 | func writeFile(f *os.File) {
40 |     fmt.Println("writing")
41 |     fmt.Fprintln(f, "data")
42 | 
43 | }
44 | 
45 | func closeFile(f *os.File) {
46 |     fmt.Println("closing")
47 |     f.Close()
48 | }
49 | 
50 |

运行结果

51 |
creating
52 | writing
53 | closing
54 | 
55 |

使用defer来调用closeFile函数可以保证在main函数结束之前,关闭文件的操作一定会被执行。

56 | 57 | -------------------------------------------------------------------------------- /GolangStudy/tutorial/go_by_example/go_environment_variable.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Go 环境变量 10 | 11 | 12 |

环境变量是一种很普遍的将配置信息传递给Unix程序的机制。

13 |
package main
14 | 
15 | import "os"
16 | import "strings"
17 | import "fmt"
18 | func main() {
19 |     // 为了设置一个key/value对,使用`os.Setenv`
20 |     // 为了获取一个key的value,使用`os.Getenv`
21 |     // 如果所提供的key在环境变量中没有对应的value,
22 |     // 那么返回空字符串
23 |     os.Setenv("FOO", "1")
24 |     fmt.Println("FOO:", os.Getenv("FOO"))
25 |     fmt.Println("BAR:", os.Getenv("BAR"))
26 | 
27 |     // 使用`os.Environ`来列出环境变量中所有的key/value对
28 |     // 你可以使用`strings.Split`方法来将key和value分开
29 |     // 这里我们打印所有的key
30 |     fmt.Println()
31 |     for _, e := range os.Environ() {
32 |         pair := strings.Split(e, "=")
33 |         fmt.Println(pair[0])
34 |     }
35 | }
36 | 
37 |

这里我们设置了FOO环境变量,所以我们取到了它的值,但是没有设置BAR环境变量,所以值为空。另外我们列出了系统的所有环境变量,当然这个输出根据不同的系统设置可能并不相同。

38 |

输出结果

39 |
FOO: 1
40 | BAR:
41 | 
42 | TERM_PROGRAM
43 | TERM
44 | SHELL
45 | TMPDIR
46 | Apple_PubSub_Socket_Render
47 | OLDPWD
48 | USER
49 | SSH_AUTH_SOCK
50 | __CF_USER_TEXT_ENCODING
51 | __CHECKFIX1436934
52 | PATH
53 | PWD
54 | ITERM_PROFILE
55 | SHLVL
56 | COLORFGBG
57 | HOME
58 | ITERM_SESSION_ID
59 | LOGNAME
60 | LC_CTYPE
61 | GOPATH
62 | _
63 | FOO
64 | 
65 | 66 | -------------------------------------------------------------------------------- /GolangStudy/tutorial/go_by_example/go_error_handle.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Go 错误处理 10 | 11 | 12 |

在Go里面通常采用显式返回错误代码的方式来进行错误处理。这个和Java或者Ruby里面使用异常或者是C里面运行正常返回结果,发生错误返回错误代码的方式不同。Go的这种错误处理的方式使得我们能够很容易看出哪些函数可能返回错误,并且能够像调用那些没有错误返回的函数一样调用。

13 |
package main
14 | 
15 | import "errors"
16 | import "fmt"
17 | 
18 | // Go语言里面约定错误代码是函数的最后一个返回值,
19 | // 并且类型是error,这是一个内置的接口
20 | 
21 | func f1(arg int) (int, error) {
22 |     if arg == 42 {
23 | 
24 |         // errors.New使用错误信息作为参数,构建一个基本的错误
25 |         return -1, errors.New("can't work with 42")
26 | 
27 |     }
28 | 
29 |     // 返回错误为nil表示没有错误
30 |     return arg + 3, nil
31 | }
32 | 
33 | // 你可以通过实现error接口的方法Error()来自定义错误
34 | // 下面我们自定义一个错误类型来表示上面例子中的参数错误
35 | type argError struct {
36 |     arg  int
37 |     prob string
38 | }
39 | 
40 | func (e *argError) Error() string {
41 |     return fmt.Sprintf("%d - %s", e.arg, e.prob)
42 | }
43 | 
44 | func f2(arg int) (int, error) {
45 |     if arg == 42 {
46 | 
47 |         // 这里我们使用&argError语法来创建一个新的结构体对象,
48 |         // 并且给它的成员赋值
49 |         return -1, &argError{arg, "can't work with it"}
50 |     }
51 |     return arg + 3, nil
52 | }
53 | 
54 | func main() {
55 | 
56 |     // 下面的两个循环例子用来测试我们的带有错误返回值的函数
57 |     // 在for循环语句里面,使用了if来判断函数返回值是否为nil是
58 |     // Go语言里面的一种约定做法。
59 |     for _, i := range []int{7, 42} {
60 |         if r, e := f1(i); e != nil {
61 |             fmt.Println("f1 failed:", e)
62 |         } else {
63 |             fmt.Println("f1 worked:", r)
64 |         }
65 |     }
66 |     for _, i := range []int{7, 42} {
67 |         if r, e := f2(i); e != nil {
68 |             fmt.Println("f2 failed:", e)
69 |         } else {
70 |             fmt.Println("f2 worked:", r)
71 |         }
72 |     }
73 | 
74 |     // 如果你需要使用自定义错误类型返回的错误数据,你需要使用类型断言
75 |     // 来获得一个自定义错误类型的实例才行。
76 |     _, e := f2(42)
77 |     if ae, ok := e.(*argError); ok {
78 |         fmt.Println(ae.arg)
79 |         fmt.Println(ae.prob)
80 |     }
81 | }
82 | 
83 |

运行结果为

84 |
f1 worked: 10
85 | f1 failed: can't work with 42
86 | f2 worked: 10
87 | f2 failed: 42 - can't work with it
88 | 42
89 | can't work with it
90 | 
91 | 92 | -------------------------------------------------------------------------------- /GolangStudy/tutorial/go_by_example/go_exit.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Go Exit 10 | 11 | 12 |

使用os.Exit可以给定一个状态,然后立刻退出程序运行。

13 |
package main
14 | 
15 | import "fmt"
16 | import "os"
17 | 
18 | func main() {
19 |     // 当使用`os.Exit`的时候defer操作不会被运行,
20 |     // 所以这里的``fmt.Println`将不会被调用
21 |     defer fmt.Println("!")
22 | 
23 |     // 退出程序并设置退出状态值
24 |     os.Exit(3)
25 | }
26 | 
27 |

注意,Go和C语言不同,main函数并不返回一个整数来表示程序的退出状态,而是将退出状态作为os.Exit函数的参数。

28 |

如果你使用go run来运行程序,将会有如下输出

29 |
exit status 3
30 | 
31 |

如果你使用go build先编译程序,然后再运行可执行文件,程序将不会有输出。 32 | 如果你想查看程序的返回值,*nix系列系统下面使用如下方法:

33 |
$ ./go_exit
34 | $ echo $?
35 | 3
36 | 
37 | 38 | -------------------------------------------------------------------------------- /GolangStudy/tutorial/go_by_example/go_for_loop.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Go for循环 10 | 11 | 12 |

for循环是Go语言唯一的循环结构。这里有三个基本的for循环类型。

13 |
package main
14 | 
15 | import "fmt"
16 | 
17 | func main() {
18 | 
19 |     // 最基本的一种,单一条件循环
20 |     // 这个可以代替其他语言的while循环
21 |     i := 1
22 |     for i <= 3 {
23 |         fmt.Println(i)
24 |         i = i + 1
25 |     }
26 | 
27 |     // 经典的循环条件初始化/条件判断/循环后条件变化
28 |     for j := 7; j <= 9; j++ {
29 |         fmt.Println(j)
30 |     }
31 | 
32 |     // 无条件的for循环是死循环,除非你使用break跳出循环或者
33 |     // 使用return从函数返回
34 |     for {
35 |         fmt.Println("loop")
36 |         break
37 |     }
38 | }
39 | 
40 |

输出结果

41 |
1
42 | 2
43 | 3
44 | 7
45 | 8
46 | 9
47 | loop
48 | 
49 |

在后面的例子中,你将会看到其他的循环方式,比如使用range函数循环数组,切片和字典,或者用select函数循环channel通道。

50 | 51 | -------------------------------------------------------------------------------- /GolangStudy/tutorial/go_by_example/go_func_callback.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Go 函数回调 10 | 11 | 12 |

Go支持函数回调,你可以把函数名称作为参数传递给另外一个函数,然后在别的地方实现这个函数。

13 |
package main
14 | 
15 | import "fmt"
16 | 
17 | type Callback func(x, y int) int
18 | 
19 | func main() {
20 |     x, y := 1, 2
21 |     fmt.Println(test(x, y, add))
22 | }
23 | 
24 | //提供一个接口,让外部去实现
25 | func test(x, y int, callback Callback) int {
26 |     return callback(x, y)
27 | }
28 | 
29 | func add(x, y int) int {
30 |     return x + y
31 | }
32 | 
33 |

运行结果

34 |
3
35 | 
36 | 37 | -------------------------------------------------------------------------------- /GolangStudy/tutorial/go_by_example/go_func_closure.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Go 闭包函数 10 | 11 | 12 |

Go支持匿名函数,匿名函数可以形成闭包。闭包函数可以访问定义闭包的函数定义的内部变量。

13 |

示例1:

14 |
package main
 15 | 
 16 | import "fmt"
 17 | 
 18 | // 这个"intSeq"函数返回另外一个在intSeq内部定义的匿名函数,
 19 | // 这个返回的匿名函数包住了变量i,从而形成了一个闭包
 20 | func intSeq() func() int {
 21 |     i := 0
 22 |     return func() int {
 23 |         i += 1
 24 |         return i
 25 |     }
 26 | }
 27 | 
 28 | func main() {
 29 |     // 我们调用intSeq函数,并且把结果赋值给一个函数nextInt,
 30 |     // 这个nextInt函数拥有自己的i变量,这个变量每次调用都被更新。
 31 |     // 这里i的初始值是由intSeq调用的时候决定的。
 32 |     nextInt := intSeq()
 33 | 
 34 |     // 调用几次nextInt,看看闭包的效果
 35 |     fmt.Println(nextInt())
 36 |     fmt.Println(nextInt())
 37 |     fmt.Println(nextInt())
 38 | 
 39 |     // 为了确认闭包的状态是独立于intSeq函数的,再创建一个。
 40 |     newInts := intSeq()
 41 |     fmt.Println(newInts())
 42 | }
 43 | 
44 |

输出结果为

45 |
1
 46 | 2
 47 | 3
 48 | 1
 49 | 
50 |

示例2:

51 |
package main
 52 | 
 53 | import "fmt"
 54 | 
 55 | func main() {
 56 |     add10 := closure(10)//其实是构造了一个加10函数
 57 |     fmt.Println(add10(5))
 58 |     fmt.Println(add10(6))
 59 |     add20 := closure(20)
 60 |     fmt.Println(add20(5))
 61 | }
 62 | 
 63 | func closure(x int) func(y int) int {
 64 |     return func(y int) int {
 65 |         return x + y
 66 |     }
 67 | 
 68 | }
 69 | 
70 |

输出结果为:

71 |
15
 72 | 16
 73 | 25
 74 | 
75 |

示例3:

76 |
package main
 77 | 
 78 | import "fmt"
 79 | 
 80 | func main() {
 81 | 
 82 |     var fs []func() int
 83 | 
 84 |     for i := 0; i < 3; i++ {
 85 | 
 86 |         fs = append(fs, func() int {
 87 | 
 88 |             return i
 89 |         })
 90 |     }
 91 |     for _, f := range fs {
 92 |         fmt.Printf("%p = %v\n", f, f())
 93 |     }
 94 | }
 95 | 
96 |

输出结果:

97 |
0x401200 = 3
 98 | 0x401200 = 3
 99 | 0x401200 = 3
100 | 
101 |

示例4:

102 |
package main
103 | 
104 | import "fmt"
105 | 
106 | func adder() func(int) int {
107 |     sum := 0
108 |     return func(x int) int {
109 |         sum += x
110 |         return sum
111 |     }
112 | }
113 | 
114 | func main() {
115 |     result := adder()
116 |     for i := 0; i < 10; i++ {
117 |         fmt.Println(result(i))
118 |     }
119 | }
120 | 
121 |

输出结果为:

122 |
0
123 | 1
124 | 3
125 | 6
126 | 10
127 | 15
128 | 21
129 | 28
130 | 36
131 | 45
132 | 
133 | 134 | -------------------------------------------------------------------------------- /GolangStudy/tutorial/go_by_example/go_func_define.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Go 函数定义 10 | 11 | 12 |

函数是Go语言的重要内容。

13 |
package main
14 | 
15 | import "fmt"
16 | 
17 | // 这个函数计算两个int型输入数据的和,并返回int型的和
18 | func plus(a int, b int) int {
19 |     // Go需要使用return语句显式地返回值
20 |     return a + b
21 | }
22 | 
23 | func main() {
24 |     // 函数的调用方式很简单
25 |     // "名称(参数列表)"
26 |     res := plus(1, 2)
27 |     fmt.Println("1+2 =", res)
28 | }
29 | 
30 |

输出结果为

31 |
1+2 = 3
32 | 
33 |

Go的函数还有很多其他的特性,其中一个就是多返回值,我们下面会看到。

34 | 35 | -------------------------------------------------------------------------------- /GolangStudy/tutorial/go_by_example/go_func_multiple_ret_value.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Go 函数多返回值 10 | 11 | 12 |

Go语言内置支持多返回值,这个在Go语言中用的很多,比如一个函数同时返回结果和错误信息。

13 |
package main
14 | 
15 | import "fmt"
16 | 
17 | // 这个函数的返回值为两个int
18 | func vals() (int, int) {
19 |     return 3, 7
20 | }
21 | 
22 | func main() {
23 | 
24 |     // 获取函数的两个返回值
25 |     a, b := vals()
26 |     fmt.Println(a)
27 |     fmt.Println(b)
28 | 
29 |     // 如果你只对多个返回值里面的几个感兴趣
30 |     // 可以使用下划线(_)来忽略其他的返回值
31 |     _, c := vals()
32 |     fmt.Println(c)
33 | }
34 | 
35 |

输出结果为

36 |
3
37 | 7
38 | 7
39 | 
40 | 41 | -------------------------------------------------------------------------------- /GolangStudy/tutorial/go_by_example/go_func_named_ret_value.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Go 函数命名返回值 10 | 11 | 12 |

函数接受参数。在 Go 中,函数可以返回多个“结果参数”,而不仅仅是一个值。它们可以像变量那样命名和使用。

13 |

如果命名了返回值参数,一个没有参数的return语句,会将当前的值作为返回值返回。注意,如果遇到if等代码块和返回值同名,还需要显示写出返回值。

14 |
package main
15 | 
16 | import "fmt"
17 | 
18 | func split(sum int) (x, y int) {
19 |     x = sum * 4 / 9
20 |     y = sum - x
21 |     return
22 | }
23 | 
24 | func main() {
25 |     fmt.Println(split(17))
26 | }
27 | 
28 |

运行结果

29 |
7 10
30 | 
31 | 32 | -------------------------------------------------------------------------------- /GolangStudy/tutorial/go_by_example/go_hello_world.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Hello World 10 | 11 | 12 | 13 |

我们的第一个例子是打印经典的“hello world”信息,我们先看下代码。

14 | 15 |
package main
16 | 
17 | import "fmt"
18 | 
19 | func main() {
20 | 	fmt.Println("hello world")
21 | }
22 | 
23 | 24 |

输出结果为:

25 | 26 |
hello world
27 | 
28 | 29 |

为了使一个go文件能够编译可执行文件,包名必须是main,然后我们导入提供格式化输出的fmt包,该程序的执行入口是func main()函数,在函数里面,我们使用fmt包提供的Println函数来输出"hello world"字符串。

30 | 31 |

为了运行这个程序,我们可以使用go run hello_world.go来运行这个例子,这样是直接输出运行结果而不会产生任何中间文件。但是有的时候我们希望能够将程序编译为二进制文件保存起来,我们可以像上面一样使用go build hello_world.go来将源代码编译为二进制可执行文件。然后我们可以直接运行这个二进制可执行文件。

32 | 33 |

好了,第一个例子就这样结束了。很简单。

34 | 35 | -------------------------------------------------------------------------------- /GolangStudy/tutorial/go_by_example/go_if_elseif.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Go if..else if..else 条件判断 10 | 11 | 12 |

Go语言的条件判断结构也很简单。

13 |
package main
14 | 
15 | import "fmt"
16 | 
17 | func main() {
18 | 
19 |     // 基本的例子
20 |     if 7%2 == 0 {
21 |         fmt.Println("7 is even")
22 |     } else {
23 |         fmt.Println("7 is odd")
24 |     }
25 | 
26 |     // 只有if条件的情况
27 |     if 8%4 == 0 {
28 |         fmt.Println("8 is divisible by 4")
29 |     }
30 | 
31 |     // if条件可以包含一个初始化表达式,这个表达式中的变量
32 |     // 是这个条件判断结构的局部变量
33 |     if num := 9; num < 0 {
34 |         fmt.Println(num, "is negative")
35 |     } else if num < 10 {
36 |         fmt.Println(num, "has 1 digit")
37 |     } else {
38 |         fmt.Println(num, "has multiple digits")
39 |     }
40 | }
41 | 
42 |

条件判断结构中,条件两边的小括号()是可以省略的,但是条件执行语句块两边的大括号{}不可以。

43 |

输出结果为

44 |
7 is odd
45 | 8 is divisible by 4
46 | 9 has 1 digit
47 | 
48 |

在Go里面没有三元表达式"?:",所以你只能使用条件判断语句。

49 | 50 | -------------------------------------------------------------------------------- /GolangStudy/tutorial/go_by_example/go_interface.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Go 接口 10 | 11 | 12 |

接口是一个方法签名的集合。 13 | 所谓方法签名,就是指方法的声明,而不包括实现。

14 |
package main
15 | 
16 | import "fmt"
17 | import "math"
18 | 
19 | // 这里定义了一个最基本的表示几何形状的方法的接口
20 | type geometry interface {
21 |     area() float64
22 |     perim() float64
23 | }
24 | 
25 | // 这里我们要让正方形square和圆形circle实现这个接口
26 | type square struct {
27 |     width, height float64
28 | }
29 | type circle struct {
30 |     radius float64
31 | }
32 | 
33 | // 在Go中实现一个接口,只要实现该接口定义的所有方法即可
34 | // 下面是正方形实现的接口
35 | func (s square) area() float64 {
36 |     return s.width * s.height
37 | }
38 | func (s square) perim() float64 {
39 |     return 2*s.width + 2*s.height
40 | }
41 | 
42 | // 圆形实现的接口
43 | func (c circle) area() float64 {
44 |     return math.Pi * c.radius * c.radius
45 | }
46 | func (c circle) perim() float64 {
47 |     return 2 * math.Pi * c.radius
48 | }
49 | 
50 | // 如果一个函数的参数是接口类型,那么我们可以使用命名接口
51 | // 来调用这个函数
52 | // 比如这里的正方形square和圆形circle都实现了接口geometry,
53 | // 那么它们都可以作为这个参数为geometry类型的函数的参数。
54 | // 在measure函数内部,Go知道调用哪个结构体实现的接口方法。
55 | func measure(g geometry) {
56 |     fmt.Println(g)
57 |     fmt.Println(g.area())
58 |     fmt.Println(g.perim())
59 | }
60 | 
61 | func main() {
62 |     s := square{width: 3, height: 4}
63 |     c := circle{radius: 5}
64 | 
65 |     // 这里circle和square都实现了geometry接口,所以
66 |     // circle类型变量和square类型变量都可以作为measure
67 |     // 函数的参数
68 |     measure(s)
69 |     measure(c)
70 | }
71 | 
72 |

输出结果为

73 |
{3 4}
74 | 12
75 | 14
76 | {5}
77 | 78.53981633974483
78 | 31.41592653589793
79 | 
80 |

也就是说如果结构体A实现了接口B定义的所有方法,那么A也是B类型。

81 | 82 | -------------------------------------------------------------------------------- /GolangStudy/tutorial/go_by_example/go_json.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Go JSON支持 10 | 11 | 12 |

Go内置了对JSON数据的编码和解码,这些数据的类型包括内置数据类型和自定义数据类型。

13 |
package main
 14 | 
 15 | import "encoding/json"
 16 | import "fmt"
 17 | import "os"
 18 | 
 19 | //  我们使用两个结构体来演示自定义数据类型的JSON数据编码和解码。
 20 | type Response1 struct {
 21 |     Page   int
 22 |     Fruits []string
 23 | }
 24 | type Response2 struct {
 25 |     Page   int      `json:"page"`
 26 |     Fruits []string `json:"fruits"`
 27 | }
 28 | 
 29 | func main() {
 30 | 
 31 |     // 首先我们看一下将基础数据类型编码为JSON数据
 32 |     bolB, _ := json.Marshal(true)
 33 |     fmt.Println(string(bolB))
 34 | 
 35 |     intB, _ := json.Marshal(1)
 36 |     fmt.Println(string(intB))
 37 | 
 38 |     fltB, _ := json.Marshal(2.34)
 39 |     fmt.Println(string(fltB))
 40 | 
 41 |     strB, _ := json.Marshal("gopher")
 42 |     fmt.Println(string(strB))
 43 | 
 44 |     // 这里是将切片和字典编码为JSON数组或对象
 45 |     slcD := []string{"apple", "peach", "pear"}
 46 |     slcB, _ := json.Marshal(slcD)
 47 |     fmt.Println(string(slcB))
 48 | 
 49 |     mapD := map[string]int{"apple": 5, "lettuce": 7}
 50 |     mapB, _ := json.Marshal(mapD)
 51 |     fmt.Println(string(mapB))
 52 | 
 53 |     // JSON包可以自动地编码自定义数据类型。结果将只包括自定义
 54 |     // 类型中的可导出成员的值并且默认情况下,这些成员名称都作
 55 |     // 为JSON数据的键
 56 |     res1D := &Response1{
 57 |         Page:   1,
 58 |         Fruits: []string{"apple", "peach", "pear"}}
 59 |     res1B, _ := json.Marshal(res1D)
 60 |     fmt.Println(string(res1B))
 61 | 
 62 |     // 你可以使用tag来自定义编码后JSON键的名称
 63 |     res2D := &Response2{
 64 |         Page:   1,
 65 |         Fruits: []string{"apple", "peach", "pear"}}
 66 |     res2B, _ := json.Marshal(res2D)
 67 |     fmt.Println(string(res2B))
 68 | 
 69 |     // 现在我们看看解码JSON数据为Go数值
 70 |     byt := []byte(`{"num":6.13,"strs":["a","b"]}`)
 71 | 
 72 |     // 我们需要提供一个变量来存储解码后的JSON数据,这里
 73 |     // 的`map[string]interface{}`将以Key-Value的方式
 74 |     // 保存解码后的数据,Value可以为任意数据类型
 75 |     var dat map[string]interface{}
 76 | 
 77 |     // 解码过程,并检测相关可能存在的错误
 78 |     if err := json.Unmarshal(byt, &dat); err != nil {
 79 |         panic(err)
 80 |     }
 81 |     fmt.Println(dat)
 82 | 
 83 |     // 为了使用解码后map里面的数据,我们需要将Value转换为
 84 |     // 它们合适的类型,例如我们将这里的num转换为期望的float64
 85 |     num := dat["num"].(float64)
 86 |     fmt.Println(num)
 87 | 
 88 |     // 访问嵌套的数据需要一些类型转换
 89 |     strs := dat["strs"].([]interface{})
 90 |     str1 := strs[0].(string)
 91 |     fmt.Println(str1)
 92 | 
 93 |     // 我们还可以将JSON解码为自定义数据类型,这有个好处是可以
 94 |     // 为我们的程序增加额外的类型安全并且不用再在访问数据的时候
 95 |     // 进行类型断言
 96 |     str := `{"page": 1, "fruits": ["apple", "peach"]}`
 97 |     res := &Response2{}
 98 |     json.Unmarshal([]byte(str), &res)
 99 |     fmt.Println(res)
100 |     fmt.Println(res.Fruits[0])
101 | 
102 |     // 上面的例子中,我们使用bytes和strings来进行原始数据和JSON数据
103 |     // 之间的转换,我们也可以直接将JSON编码的数据流写入`os.Writer`
104 |     // 或者是HTTP请求回复数据。
105 |     enc := json.NewEncoder(os.Stdout)
106 |     d := map[string]int{"apple": 5, "lettuce": 7}
107 |     enc.Encode(d)
108 | }
109 | 
110 |

运行结果

111 |
true
112 | 1
113 | 2.34
114 | "gopher"
115 | ["apple","peach","pear"]
116 | {"apple":5,"lettuce":7}
117 | {"Page":1,"Fruits":["apple","peach","pear"]}
118 | {"page":1,"fruits":["apple","peach","pear"]}
119 | map[num:6.13 strs:[a b]]
120 | 6.13
121 | a
122 | &{1 [apple peach]}
123 | apple
124 | {"apple":5,"lettuce":7}
125 | 
126 | 127 | -------------------------------------------------------------------------------- /GolangStudy/tutorial/go_by_example/go_line_filters.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Go Line Filters 10 | 11 | 12 |

Line Filters翻译一下大概是行数据过滤器。简单一点就是一个程序从标准输入stdin读取数据,然后处理一下,将处理的结果输出到标准输出stdout。grep和sed就是常见的行数据过滤器。 13 | 这里有一个行数据过滤器的例子,是把一个输入文本转换为大写的文本。你可以使用这种方式来实现你自己的Go Line Filters。

14 |
package main
15 | 
16 | import (
17 |     "bufio"
18 |     "fmt"
19 |     "os"
20 |     "strings"
21 | )
22 | 
23 | func main() {
24 | 
25 |     // 使用缓冲scanner来包裹无缓冲的`os.Stdin`可以让我们
26 |     // 方便地使用`Scan`方法,这个方法会将scanner定位到下
27 |     // 一行的位置
28 |     scanner := bufio.NewScanner(os.Stdin)
29 | 
30 |     for scanner.Scan() {
31 |         // `Text`方法从输入中返回当前行
32 |         ucl := strings.ToUpper(scanner.Text())
33 | 
34 |         // 输出转换为大写的行
35 |         fmt.Println(ucl)
36 |     }
37 | 
38 |     // 在`Scan`过程中,检查错误。文件结束不会被当作一个错误
39 |     if err := scanner.Err(); err != nil {
40 |         fmt.Fprintln(os.Stderr, "error:", err)
41 |         os.Exit(1)
42 |     }
43 | }
44 | 
45 |

运行结果

46 |
hello world
47 | HELLO WORLD
48 | how are you
49 | HOW ARE YOU
50 | 
51 | 52 | -------------------------------------------------------------------------------- /GolangStudy/tutorial/go_by_example/go_map.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Go 字典 10 | 11 | 12 |

字典是Go语言内置的关联数据类型。因为数组是索引对应数组元素,而字典是键对应值。

13 |
package main
14 | 
15 | import "fmt"
16 | 
17 | func main() {
18 | 
19 |     // 创建一个字典可以使用内置函数make
20 |     // "make(map[键类型]值类型)"
21 |     m := make(map[string]int)
22 | 
23 |     // 使用经典的"name[key]=value"来为键设置值
24 |     m["k1"] = 7
25 |     m["k2"] = 13
26 | 
27 |     // 用Println输出字典,会输出所有的键值对
28 |     fmt.Println("map:", m)
29 | 
30 |     // 获取一个键的值 "name[key]".
31 |     v1 := m["k1"]
32 |     fmt.Println("v1: ", v1)
33 | 
34 |     // 内置函数返回字典的元素个数
35 |     fmt.Println("len:", len(m))
36 | 
37 |     // 内置函数delete从字典删除一个键对应的值
38 |     delete(m, "k2")
39 |     fmt.Println("map:", m)
40 | 
41 |     // 根据键来获取值有一个可选的返回值,这个返回值表示字典中是否
42 |     // 存在该键,如果存在为true,返回对应值,否则为false,返回零值
43 |     // 有的时候需要根据这个返回值来区分返回结果到底是存在的值还是零值
44 |     // 比如字典不存在键x对应的整型值,返回零值就是0,但是恰好字典中有
45 |     // 键y对应的值为0,这个时候需要那个可选返回值来判断是否零值。
46 |     _, ok := m["k2"]
47 |     fmt.Println("ok:", ok)
48 | 
49 |     // 你可以用 ":=" 同时定义和初始化一个字典
50 |     n := map[string]int{"foo": 1, "bar": 2}
51 |     fmt.Println("map:", n)
52 | }
53 | 
54 |

输出结果为

55 |
map: map[k1:7 k2:13]
56 | v1:  7
57 | len: 2
58 | map: map[k1:7]
59 | ok: false
60 | map: map[foo:1 bar:2]
61 | 
62 | 63 | -------------------------------------------------------------------------------- /GolangStudy/tutorial/go_by_example/go_method.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Go 方法 10 | 11 | 12 |

一般的函数定义叫做函数,定义在结构体上面的函数叫做该结构体的方法。

13 |

示例1:

14 |
package main
 15 | 
 16 | import "fmt"
 17 | 
 18 | type rect struct {
 19 |     width, height int
 20 | }
 21 | 
 22 | // 这个area方法有一个限定类型*rect,
 23 | // 表示这个函数是定义在rect结构体上的方法
 24 | func (r *rect) area() int {
 25 |     return r.width * r.height
 26 | }
 27 | 
 28 | // 方法的定义限定类型可以为结构体类型
 29 | // 也可以是结构体指针类型
 30 | // 区别在于如果限定类型是结构体指针类型
 31 | // 那么在该方法内部可以修改结构体成员信息
 32 | func (r rect) perim() int {
 33 |     return 2*r.width + 2*r.height
 34 | }
 35 | 
 36 | func main() {
 37 |     r := rect{width: 10, height: 5}
 38 | 
 39 |     // 调用方法
 40 |     fmt.Println("area: ", r.area())
 41 |     fmt.Println("perim:", r.perim())
 42 | 
 43 |     // Go语言会自动识别方法调用的参数是结构体变量还是
 44 |     // 结构体指针,如果你要修改结构体内部成员值,那么使用
 45 |     // 结构体指针作为函数限定类型,也就是说参数若是结构体
 46 |     //变量,仅仅会发生值拷贝。
 47 |     rp := &r
 48 |     fmt.Println("area: ", rp.area())
 49 |     fmt.Println("perim:", rp.perim())
 50 | }
 51 | 
52 |

输出结果为

53 |
area:  50
 54 | perim: 30
 55 | area:  50
 56 | perim: 30
 57 | 
58 |

示例2:

59 |

从某种意义上说,方法是函数的“语法糖”。当函数与某个特定的类型绑定,那么它就是一个方法。也证因为如此,我们可以将方法“还原”成函数。

60 |

instance.method(args)->(type).func(instance,args)

61 |

为了区别这两种方式,官方文档中将左边的称为Method Value,右边则是Method Expression。Method Value是包装后的状态对象,总是与特定的对象实例关联在一起(类似闭包,拐带私奔),而Method Expression函数将Receiver作为第一个显式参数,调用时需额外传递。

62 |

注意:对于Method Expression,T仅拥有T Receiver方法,T拥有(T+T)所有方法。

63 |
package main
 64 | 
 65 | import (
 66 |     "fmt"
 67 | )
 68 | 
 69 | func main() {
 70 |     p := Person{2, "张三"}
 71 | 
 72 |     p.test(1)
 73 |     var f1 func(int) = p.test
 74 |     f1(2)
 75 |     Person.test(p, 3)
 76 |     var f2 func(Person, int) = Person.test
 77 |     f2(p, 4)
 78 | 
 79 | }
 80 | 
 81 | type Person struct {
 82 |     Id   int
 83 |     Name string
 84 | }
 85 | 
 86 | func (this Person) test(x int) {
 87 |     fmt.Println("Id:", this.Id, "Name", this.Name)
 88 |     fmt.Println("x=", x)
 89 | }
 90 | 
91 |

输出结果:

92 |
Id: 2 Name 张三
 93 | x= 1
 94 | Id: 2 Name 张三
 95 | x= 2
 96 | Id: 2 Name 张三
 97 | x= 3
 98 | Id: 2 Name 张三
 99 | x= 4
100 | 
101 |

示例3:

102 |

使用匿名字段,实现模拟继承。即可直接访问匿名字段(匿名类型或匿名指针类型)的方法这种行为类似“继承”。访问匿名字段方法时,有隐藏规则,这样我们可以实现override效果。

103 |
package main
104 | 
105 | import (
106 |     "fmt"
107 | )
108 | 
109 | func main() {
110 |     p := Student{Person{2, "张三"}, 25}
111 |     p.test()
112 | 
113 | }
114 | 
115 | type Person struct {
116 |     Id   int
117 |     Name string
118 | }
119 | 
120 | type Student struct {
121 |     Person
122 |     Score int
123 | }
124 | 
125 | func (this Person) test() {
126 |     fmt.Println("person test")
127 | }
128 | 
129 | func (this Student) test() {
130 |     fmt.Println("student test")
131 | }
132 | 
133 |

输出结果为:

134 |
student test
135 | 
136 | 137 | -------------------------------------------------------------------------------- /GolangStudy/tutorial/go_by_example/go_mutex.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Go 互斥 10 | 11 | 12 |

上面的例子中,我们看过了如何在多个协程之间原子地访问计数器,对于更复杂的例子,我们可以使用Mutex来在多个协程之间安全地访问数据。

13 |
package main
14 | 
15 | import (
16 |     "fmt"
17 |     "math/rand"
18 |     "runtime"
19 |     "sync"
20 |     "sync/atomic"
21 |     "time"
22 | )
23 | 
24 | func main() {
25 | 
26 |     // 这个例子的状态就是一个map
27 |     var state = make(map[int]int)
28 | 
29 |     // 这个`mutex`将同步对状态的访问
30 |     var mutex = &sync.Mutex{}
31 | 
32 |     // ops将对状态的操作进行计数
33 |     var ops int64 = 0
34 |     // 这里我们启动100个协程来不断地读取这个状态
35 |     for r := 0; r < 100; r++ {
36 |         go func() {
37 |             total := 0
38 |             for {
39 | 
40 |                 // 对于每次读取,我们选取一个key来访问,
41 |                 // mutex的`Lock`函数用来保证对状态的
42 |                 // 唯一性访问,访问结束后,使用`Unlock`
43 |                 // 来解锁,然后增加ops计数器
44 |                 key := rand.Intn(5)
45 |                 mutex.Lock()
46 |                 total += state[key]
47 |                 mutex.Unlock()
48 |                 atomic.AddInt64(&ops, 1)
49 | 
50 |                 // 为了保证这个协程不会让调度器出于饥饿状态,
51 |                 // 我们显式地使用`runtime.Gosched`释放了资源
52 |                 // 控制权,这种控制权会在通道操作结束或者
53 |                 // time.Sleep结束后自动释放。但是这里我们需要
54 |                 // 手动地释放资源控制权
55 |                 runtime.Gosched()
56 |             }
57 |         }()
58 |     }
59 |     // 同样我们使用10个协程来模拟写状态
60 |     for w := 0; w < 10; w++ {
61 |         go func() {
62 |             for {
63 |                 key := rand.Intn(5)
64 |                 val := rand.Intn(100)
65 |                 mutex.Lock()
66 |                 state[key] = val
67 |                 mutex.Unlock()
68 |                 atomic.AddInt64(&ops, 1)
69 |                 runtime.Gosched()
70 |             }
71 |         }()
72 |     }
73 | 
74 |     // 主协程Sleep,让那10个协程能够运行一段时间
75 |     time.Sleep(time.Second)
76 | 
77 |     // 输出总操作次数
78 |     opsFinal := atomic.LoadInt64(&ops)
79 |     fmt.Println("ops:", opsFinal)
80 | 
81 |     // 最后锁定并输出状态
82 |     mutex.Lock()
83 |     fmt.Println("state:", state)
84 |     mutex.Unlock()
85 | }
86 | 
87 |

运行结果

88 |
ops: 3931611
89 | state: map[0:84 2:20 3:18 1:65 4:31]
90 | 
91 | 92 | -------------------------------------------------------------------------------- /GolangStudy/tutorial/go_by_example/go_non_blocking_channel.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Go 非阻塞通道 10 | 11 | 12 |

默认情况下,通道发送和接收数据是阻塞的。然而我们可以使用select的一个default的选项来实现无阻塞发送或接收数据,甚至可以将多个select的case选项和default选项结合起来使用。

13 |
package main
 14 | 
 15 | import "fmt"
 16 | 
 17 | func main() {
 18 |     messages := make(chan string)
 19 |     signals := make(chan bool)
 20 | 
 21 |     // 这里是一个非阻塞的从通道接收数据,如果messages通道有数据
 22 |     // 可以接收,那么select将运行`<-messages`这个case,否则的话
 23 |     // 程序立刻执行default选项后面的语句
 24 |     select {
 25 |     case msg := <-messages:
 26 |         fmt.Println("received message", msg)
 27 |     default:
 28 |         fmt.Println("no message received")
 29 |     }
 30 | 
 31 |     // 非阻塞通道发送数据也是一样的
 32 |     msg := "hi"
 33 |     select {
 34 |     case messages <- msg:
 35 |         fmt.Println("sent message", msg)
 36 |     default:
 37 |         fmt.Println("no message sent")
 38 |     }
 39 | 
 40 |     // 在default前面,我们可以有多个case选项,从而实现多通道
 41 |     // 非阻塞的选择,这里我们尝试从messages和signals接收数据
 42 |     // 如果有数据可以接收,那么执行对应case后面的逻辑,否则立刻
 43 |     // 执行default选项后面的逻辑
 44 |     select {
 45 |     case msg := <-messages:
 46 |         fmt.Println("received message", msg)
 47 |     case sig := <-signals:
 48 |         fmt.Println("received signal", sig)
 49 |     default:
 50 |         fmt.Println("no activity")
 51 |     }
 52 | }
 53 | 
54 |

运行结果

55 |
no message received
 56 | no message sent
 57 | no activity
 58 | 
59 |

这个例子中,由于我们使用了default来实现非阻塞的通道,所以开始的时候messages里面没有数据可以接收,直接输出no message received,而第二次由于messages通道没有相应的数据接收方,所以也不会写入数据,直接转到default,输出no message sent,至于第三个就很好理解了,什么也没有,输出no activity。 60 | 其实,我们可以把这个例子修改一下,让messages通道带缓冲区,这样例子或许更好理解一点。定义messages的时候使用messages := make(chan string, 1)

61 |
package main
 62 | 
 63 | import "fmt"
 64 | 
 65 | func main() {
 66 |     messages := make(chan string, 1)
 67 |     signals := make(chan bool)
 68 | 
 69 |     // 这里是一个非阻塞的从通道接收数据,如果messages通道有数据
 70 |     // 可以接收,那么select将运行`<-messages`这个case,否则的话
 71 |     // 程序立刻执行default选项后面的语句
 72 |     select {
 73 |     case msg := <-messages:
 74 |         fmt.Println("received message", msg)
 75 |     default:
 76 |         fmt.Println("no message received")
 77 |     }
 78 | 
 79 |     // 非阻塞通道发送数据也是一样的,但是由于messages带了缓冲区,
 80 |     // 即使没有数据接受端也可以发送数据,所以这里的`messages<-msg`
 81 |     // 会被执行,从而不再跳到default去了。
 82 |     msg := "hi"
 83 |     select {
 84 |     case messages <- msg:
 85 |         fmt.Println("sent message", msg)
 86 |     default:
 87 |         fmt.Println("no message sent")
 88 |     }
 89 | 
 90 |     // 在default前面,我们可以有多个case选项,从而实现多通道
 91 |     // 非阻塞的选择,这里我们尝试从messages和signals接收数据
 92 |     // 如果有数据可以接收,那么执行对应case后面的逻辑,否则立刻
 93 |     // 执行default选项后面的逻辑
 94 |     select {
 95 |     case msg := <-messages:
 96 |         fmt.Println("received message", msg)
 97 |     case sig := <-signals:
 98 |         fmt.Println("received signal", sig)
 99 |     default:
100 |         fmt.Println("no activity")
101 |     }
102 | }
103 | 
104 |

运行结果

105 |
no message received
106 | sent message hi
107 | received message hi
108 | 
109 | 110 | -------------------------------------------------------------------------------- /GolangStudy/tutorial/go_by_example/go_number.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Go 数值 10 | 11 | 12 |

Go有很多种数据类型,包括字符串类型,整型,浮点型,布尔型等等,这里有几个基础的例子。

13 |
package main
14 | 
15 | import "fmt"
16 | 
17 | func main() {
18 | 
19 |     // 字符串可以使用"+"连接
20 |     fmt.Println("go" + "lang")
21 | 
22 |     //整型和浮点型
23 |     fmt.Println("1+1 =", 1+1)
24 |     fmt.Println("7.0/3.0 =", 7.0/3.0)
25 | 
26 |     // 布尔型的几种操作符
27 |     fmt.Println(true && false)
28 |     fmt.Println(true || false)
29 |     fmt.Println(!true)
30 | }
31 | 
32 |

输出结果为

33 |
golang
34 | 1+1 = 2
35 | 7.0/3.0 = 2.3333333333333335
36 | false
37 | true
38 | false
39 | 
40 | 41 | -------------------------------------------------------------------------------- /GolangStudy/tutorial/go_by_example/go_number_parse.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Go 数字解析 10 | 11 | 12 |

从字符串解析出数字是一个基本的而且很常见的任务。 13 | Go内置的strconv提供了数字解析功能。

14 |
package main
15 | 
16 | import "strconv"
17 | import "fmt"
18 | 
19 | func main() {
20 |     // 使用ParseFloat解析浮点数,64是说明使用多少位
21 |     // 精度来解析
22 |     f, _ := strconv.ParseFloat("1.234", 64)
23 |     fmt.Println(f)
24 | 
25 |     // 对于ParseInt函数,0 表示从字符串推断整型进制,
26 |     // 则表示返回结果的位数
27 |     i, _ := strconv.ParseInt("123", 0, 64)
28 |     fmt.Println(i)
29 | 
30 |     // ParseInt能够解析出16进制的数字
31 |     d, _ := strconv.ParseInt("0x1c8", 0, 64)
32 |     fmt.Println(d)
33 | 
34 |     // 还可以使用ParseUint函数
35 |     u, _ := strconv.ParseUint("789", 0, 64)
36 |     fmt.Println(u)
37 | 
38 |     // Atoi是解析10进制整型的快捷方法
39 |     k, _ := strconv.Atoi("135")
40 |     fmt.Println(k)
41 | 
42 |     // 解析函数在遇到无法解析的输入时,会返回错误
43 |     _, e := strconv.Atoi("wat")
44 |     fmt.Println(e)
45 | }
46 | 
47 |

运行结果

48 |
1.234
49 | 123
50 | 456
51 | 789
52 | 135
53 | strconv.ParseInt: parsing "wat": invalid syntax
54 | 
55 | 56 | -------------------------------------------------------------------------------- /GolangStudy/tutorial/go_by_example/go_panic.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Go Panic 10 | 11 | 12 |

Panic表示的意思就是有些意想不到的错误发生了。通常我们用来表示程序正常运行过程中不应该出现的,或者我们没有处理好的错误。

13 |
package main
14 | 
15 | import "os"
16 | 
17 | func main() {
18 | 
19 |     // 我们使用panic来检查预期不到的错误
20 |     panic("a problem")
21 | 
22 |     // Panic的通常使用方法就是如果一个函数
23 |     // 返回一个我们不知道怎么处理的错误的
24 |     // 时候,直接终止执行。
25 |     _, err := os.Create("/tmp/file")
26 |     if err != nil {
27 |         panic(err)
28 |     }
29 | }
30 | 
31 |

运行结果

32 |
panic: a problem
33 | 
34 | goroutine 1 [running]:
35 | runtime.panic(0x44e060, 0xc0840031b0)
36 |         C:/Users/ADMINI~1/AppData/Local/Temp/2/bindist667667715/go/src/pkg/runtime/panic.c:266 +0xc8
37 | main.main()
38 |         D:/GoDoc/go_panic.go:8 +0x58
39 | exit status 2
40 | 
41 |

和其他的编程语言不同的是,Go并不使用exception来处理错误,而是通过函数返回值返回错误代码。

42 | 43 | -------------------------------------------------------------------------------- /GolangStudy/tutorial/go_by_example/go_parallel.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Go 并行功能 10 | 11 | 12 |

goroutine是一个轻量级的线程。

13 |
package main
14 | 
15 | import "fmt"
16 | 
17 | func f(from string) {
18 |     for i := 0; i < 3; i++ {
19 |         fmt.Println(from, ":", i)
20 |     }
21 | }
22 | 
23 | func main() {
24 | 
25 |     // 假设我们有一个函数叫做f(s)
26 |     // 这里我们使用通常的同步调用来调用函数
27 |     f("direct")
28 | 
29 |     // 为了能够让这个函数以协程(goroutine)方式
30 |     // 运行使用go f(s)
31 |     // 这个协程将和调用它的协程并行执行
32 |     go f("goroutine")
33 | 
34 |     // 你也可以为匿名函数开启一个协程运行
35 |     go func(msg string) {
36 |         fmt.Println(msg)
37 |     }("going")
38 | 
39 |     // 上面的协程在调用之后就异步执行了,所以程序不用等待它们执行完成
40 |     // 就跳到这里来了,下面的Scanln用来从命令行获取一个输入,然后才
41 |     // 让main函数结束
42 |     // 如果没有下面的Scanln语句,程序到这里会直接退出,而上面的协程还
43 |     // 没有来得及执行完,你将无法看到上面两个协程运行的结果
44 |     var input string
45 |     fmt.Scanln(&input)
46 |     fmt.Println("done")
47 | }
48 | 
49 |

运行结果

50 |
direct : 0
51 | direct : 1
52 | direct : 2
53 | goroutine : 0
54 | goroutine : 1
55 | goroutine : 2
56 | going
57 | ok
58 | done
59 | 
60 | 61 | -------------------------------------------------------------------------------- /GolangStudy/tutorial/go_by_example/go_parallel_channel.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Go 并行通道Channel 10 | 11 | 12 |

Channel是连接并行协程(goroutine)的通道。你可以向一个通道写入数据然后从另外一个通道读取数据。

13 |
package main
14 | 
15 | import "fmt"
16 | 
17 | func main() {
18 | 
19 |     // 使用`make(chan 数据类型)`来创建一个Channel
20 |     // Channel的类型就是它们所传递的数据的类型
21 |     messages := make(chan string)
22 | 
23 |     // 使用`channel <-`语法来向一个Channel写入数据
24 |     // 这里我们从一个新的协程向messages通道写入数据ping
25 |     go func() { messages <- "ping" }()
26 | 
27 |     // 使用`<-channel`语法来从Channel读取数据
28 |     // 这里我们从main函数所在的协程来读取刚刚写入
29 |     // messages通道的数据
30 |     msg := <-messages
31 |     fmt.Println(msg)
32 | }
33 | 
34 |

运行结果

35 |
ping
36 | 
37 |

当我们运行程序的时候,数据ping成功地从一个协程传递到了另外一个协程。 38 | 默认情况下,协程之间的通信是同步的,也就是说数据的发送端和接收端必须配对使用。Channel的这种特点使得我们可以不用在程序结尾添加额外的代码也能够获取协程发送端发来的信息。因为程序执行到msg:=<-messages的时候被阻塞了,直到获得发送端发来的信息才继续执行。

39 | 40 | -------------------------------------------------------------------------------- /GolangStudy/tutorial/go_by_example/go_pointer.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Go 指针 10 | 11 | 12 |

Go支持指针,可以用来给函数传递变量的引用。

13 |
package main
14 | 
15 | import "fmt"
16 | 
17 | // 我们用两个不同的例子来演示指针的用法
18 | // zeroval函数有一个int类型参数,这个时候传递给函数的是变量的值
19 | func zeroval(ival int) {
20 |     ival = 0
21 | }
22 | 
23 | // zeroptr函数的参数是int类型指针,这个时候传递给函数的是变量的地址
24 | // 在函数内部对这个地址所指向的变量的任何修改都会反映到原来的变量上。
25 | func zeroptr(iptr *int) {
26 |     *iptr = 0
27 | }
28 | 
29 | func main() {
30 |     i := 1
31 |     fmt.Println("initial:", i)
32 | 
33 |     zeroval(i)
34 |     fmt.Println("zeroval:", i)
35 | 
36 |     // &操作符用来取得i变量的地址
37 |     zeroptr(&i)
38 |     fmt.Println("zeroptr:", i)
39 | 
40 |     // 指针类型也可以输出
41 |     fmt.Println("pointer:", &i)
42 | }
43 | 
44 |

输出结果为

45 |
initial: 1
46 | zeroval: 1
47 | zeroptr: 0
48 | pointer: 0xc084000038
49 | 
50 | 51 | -------------------------------------------------------------------------------- /GolangStudy/tutorial/go_by_example/go_process_run.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Go 进程执行 10 | 11 | 12 |

在上面的例子中,我们演示了一下如何去触发执行一个外部的进程。我们这样做的原因是我们希望从Go进程里面可以访问外部进程的信息。但有的时候,我们仅仅希望执行一个外部进程来替代当前的Go进程。这个时候,我们需要使用Go提供的exec函数。

13 |
package main
14 | 
15 | import "syscall"
16 | import "os"
17 | import "os/exec"
18 | 
19 | func main() {
20 | 
21 |     // 本例中,我们使用`ls`来演示。Go需要一个该命令
22 |     // 的完整路径,所以我们使用`exec.LookPath`函数来
23 |     // 找到它
24 |     binary, lookErr := exec.LookPath("ls")
25 |     if lookErr != nil {
26 |         panic(lookErr)
27 |     }
28 |     // `Exec`函数需要一个切片参数,我们给ls命令一些
29 |     // 常见的参数。注意,第一个参数必须是程序名称
30 |     args := []string{"ls", "-a", "-l", "-h"}
31 | 
32 |     // `Exec`还需要一些环境变量,这里我们提供当前的
33 |     // 系统环境
34 |     env := os.Environ()
35 | 
36 |     // 这里是`os.Exec`调用。如果一切顺利,我们的原
37 |     // 进程将终止,然后启动一个新的ls进程。如果有
38 |     // 错误发生,我们将获得一个返回值
39 |     execErr := syscall.Exec(binary, args, env)
40 |     if execErr != nil {
41 |         panic(execErr)
42 |     }
43 | }
44 | 
45 |

运行结果

46 |
total 16
47 | drwxr-xr-x  4 mark 136B Oct 3 16:29 .
48 | drwxr-xr-x 91 mark 3.0K Oct 3 12:50 ..
49 | -rw-r--r--  1 mark 1.3K Oct 3 16:28 execing-processes.go
50 | 
51 |

注意,Go没有提供Unix下面经典的fork函数。通常这也不是一个问题,因为进程触发和进程执行已经覆盖了fork的大多数功能。

52 | 53 | -------------------------------------------------------------------------------- /GolangStudy/tutorial/go_by_example/go_process_trigger.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Go 进程触发 10 | 11 | 12 |

有的时候,我们需要从Go程序里面触发一个其他的非Go进程来执行。

13 |
package main
14 | 
15 | import "fmt"
16 | import "io/ioutil"
17 | import "os/exec"
18 | 
19 | func main() {
20 | 
21 |     // 我们从一个简单的命令开始,这个命令不需要任何参数
22 |     // 或者输入,仅仅向stdout输出一些信息。`exec.Command`
23 |     // 函数创建了一个代表外部进程的对象
24 |     dateCmd := exec.Command("date")
25 | 
26 |     // `Output`是另一个运行命令时用来处理信息的函数,这个
27 |     // 函数等待命令结束,然后收集命令输出。如果没有错误发
28 |     // 生的话,`dateOut`将保存date的信息
29 |     dateOut, err := dateCmd.Output()
30 |     if err != nil {
31 |         panic(err)
32 |     }
33 |     fmt.Println("> date")
34 |     fmt.Println(string(dateOut))
35 | 
36 |     // 下面我们看一个需要从stdin输入数据的命令,我们将
37 |     // 数据输入传给外部进程的stdin,然后从它输出到stdout
38 |     // 的运行结果收集信息
39 |     grepCmd := exec.Command("grep", "hello")
40 | 
41 |     // 这里我们显式地获取input/output管道,启动进程,
42 |     // 向进程写入数据,然后读取输出结果,最后等待进程结束
43 |     grepIn, _ := grepCmd.StdinPipe()
44 |     grepOut, _ := grepCmd.StdoutPipe()
45 |     grepCmd.Start()
46 |     grepIn.Write([]byte("hello grep\ngoodbye grep"))
47 |     grepIn.Close()
48 |     grepBytes, _ := ioutil.ReadAll(grepOut)
49 |     grepCmd.Wait()
50 | 
51 |     // 在上面的例子中,我们忽略了错误检测,但是你一样可以
52 |     // 使用`if err!=nil`模式来进行处理。另外我们仅仅收集了
53 |     // `StdoutPipe`的结果,同时你也可以用一样的方法来收集
54 |     // `StderrPipe`的结果
55 |     fmt.Println("> grep hello")
56 |     fmt.Println(string(grepBytes))
57 | 
58 |     // 注意,我们在触发外部命令的时候,需要显式地提供
59 |     // 命令和参数信息。另外如果你想用一个命令行字符串
60 |     // 触发一个完整的命令,你可以使用bash的-c选项
61 |     lsCmd := exec.Command("bash", "-c", "ls -a -l -h")
62 |     lsOut, err := lsCmd.Output()
63 |     if err != nil {
64 |         panic(err)
65 |     }
66 |     fmt.Println("> ls -a -l -h")
67 |     fmt.Println(string(lsOut))
68 | }
69 | 
70 |

所触发的程序的执行结果和我们直接执行这些程序的结果是一样的。 71 | 运行结果

72 |
> date
73 | Wed Oct 10 09:53:11 PDT 2012
74 | > grep hello
75 | hello grep
76 | > ls -a -l -h
77 | drwxr-xr-x  4 mark 136B Oct 3 16:29 .
78 | drwxr-xr-x 91 mark 3.0K Oct 3 12:50 ..
79 | -rw-r--r--  1 mark 1.3K Oct 3 16:28 spawning-processes.go
80 | 
81 | 82 | -------------------------------------------------------------------------------- /GolangStudy/tutorial/go_by_example/go_rand_number.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Go 随机数 10 | 11 | 12 |

Go的math/rand包提供了伪随机数的生成。

13 |
package main
14 | 
15 | import "fmt"
16 | import "math/rand"
17 | 
18 | func main() {
19 | 
20 |     // 例如`rand.Intn`返回一个整型随机数n,0<=n<100
21 |     fmt.Print(rand.Intn(100), ",")
22 |     fmt.Print(rand.Intn(100))
23 |     fmt.Println()
24 | 
25 |     // `rand.Float64` 返回一个`float64` `f`,
26 |     // `0.0 <= f < 1.0`
27 |     fmt.Println(rand.Float64())
28 | 
29 |     // 这个方法可以用来生成其他数值范围内的随机数,
30 |     // 例如`5.0 <= f < 10.0`
31 |     fmt.Print((rand.Float64()*5)+5, ",")
32 |     fmt.Print((rand.Float64() * 5) + 5)
33 |     fmt.Println()
34 | 
35 |     // 为了使随机数生成器具有确定性,可以给它一个seed
36 |     s1 := rand.NewSource(42)
37 |     r1 := rand.New(s1)
38 | 
39 |     fmt.Print(r1.Intn(100), ",")
40 |     fmt.Print(r1.Intn(100))
41 |     fmt.Println()
42 | 
43 |     // 如果源使用一个和上面相同的seed,将生成一样的随机数
44 |     s2 := rand.NewSource(42)
45 |     r2 := rand.New(s2)
46 |     fmt.Print(r2.Intn(100), ",")
47 |     fmt.Print(r2.Intn(100))
48 |     fmt.Println()
49 | }
50 | 
51 |

运行结果

52 |
81,87
53 | 0.6645600532184904
54 | 7.1885709359349015,7.123187485356329
55 | 5,87
56 | 5,87
57 | 
58 | 59 | -------------------------------------------------------------------------------- /GolangStudy/tutorial/go_by_example/go_range.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Go range函数 10 | 11 | 12 |

range函数是个神奇而有趣的内置函数,你可以使用它来遍历数组,切片和字典。

13 |

当用于遍历数组和切片的时候,range函数返回索引和元素;

14 |

当用于遍历字典的时候,range函数返回字典的键和值。

15 |
package main
16 | 
17 | import "fmt"
18 | 
19 | func main() {
20 | 
21 |     // 这里我们使用range来计算一个切片的所有元素和
22 |     // 这种方法对数组也适用
23 |     nums := []int{2, 3, 4}
24 |     sum := 0
25 |     for _, num := range nums {
26 |         sum += num
27 |     }
28 |     fmt.Println("sum:", sum)
29 | 
30 |     // range 用来遍历数组和切片的时候返回索引和元素值
31 |     // 如果我们不要关心索引可以使用一个下划线(_)来忽略这个返回值
32 |     // 当然我们有的时候也需要这个索引
33 |     for i, num := range nums {
34 |         if num == 3 {
35 |             fmt.Println("index:", i)
36 |         }
37 |     }
38 | 
39 |     // 使用range来遍历字典的时候,返回键值对。
40 |     kvs := map[string]string{"a": "apple", "b": "banana"}
41 |     for k, v := range kvs {
42 |         fmt.Printf("%s -> %s\n", k, v)
43 |     }
44 | 
45 |     // range函数用来遍历字符串时,返回Unicode代码点。
46 |     // 第一个返回值是每个字符的起始字节的索引,第二个是字符代码点,
47 |     // 因为Go的字符串是由字节组成的,多个字节组成一个rune类型字符。
48 |     for i, c := range "go" {
49 |         fmt.Println(i, c)
50 |     }
51 | }
52 | 
53 |

输出结果为

54 |
sum: 9
55 | index: 1
56 | a -> apple
57 | b -> banana
58 | 0 103
59 | 1 111
60 | 
61 | 62 | -------------------------------------------------------------------------------- /GolangStudy/tutorial/go_by_example/go_read_file.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Go 读取文件 10 | 11 | 12 |

读写文件是很多程序的基本任务,下面我们看看Go里面的文件读取。

13 |
package main
14 | 
15 | import (
16 |     "bufio"
17 |     "fmt"
18 |     "io"
19 |     "io/ioutil"
20 |     "os"
21 | )
22 | 
23 | // 读取文件的函数调用大多数都需要检查错误,
24 | // 使用下面这个错误检查方法可以方便一点
25 | func check(e error) {
26 |     if e != nil {
27 |         panic(e)
28 |     }
29 | }
30 | 
31 | func main() {
32 | 
33 |     // 最基本的文件读写任务就是把整个文件的内容读取到内存
34 |     dat, err := ioutil.ReadFile("/tmp/dat")
35 |     check(err)
36 |     fmt.Print(string(dat))
37 | 
38 |     // 有的时候你想更多地控制到底是读取文件的哪个部分,这个
39 |     // 时候你可以使用`os.Open`打开一个文件获取一个`os.File`
40 |     // 对象
41 |     f, err := os.Open("/tmp/dat")
42 | 
43 |     // 从这个文件中读取一些字节,并且由于字节数组长度所限,
44 |     // 最多读取5个字节,另外还需要注意实际能够读取的字节
45 |     // 数量
46 |     b1 := make([]byte, 5)
47 |     n1, err := f.Read(b1)
48 |     check(err)
49 |     fmt.Printf("%d bytes: %s\n", n1, string(b1))
50 | 
51 |     // 你也可以使用`Seek`来跳转到文件中的一个已知位置,并从
52 |     // 那个位置开始读取数据
53 |     o2, err := f.Seek(6, 0)
54 |     check(err)
55 |     b2 := make([]byte, 2)
56 |     n2, err := f.Read(b2)
57 |     check(err)
58 |     fmt.Printf("%d bytes @ %d: %s\n", n2, o2, string(b2))
59 | 
60 |     // `io`包提供了一些帮助文件读取的函数。例如上面的方法如果
61 |     // 使用方法`ReadAtLeast`函数来实现,将使得程序更健壮
62 |     o3, err := f.Seek(6, 0)
63 |     check(err)
64 |     b3 := make([]byte, 2)
65 |     n3, err := io.ReadAtLeast(f, b3, 2)
66 |     check(err)
67 |     fmt.Printf("%d bytes @ %d: %s\n", n3, o3, string(b3))
68 | 
69 |     // 没有内置的rewind方法,但是可以使用`Seek(0,0)`来实现
70 |     _, err = f.Seek(0, 0)
71 |     check(err)
72 | 
73 |     // `bufio`包提供了缓冲读取文件的方法,这将使得文件读取更加
74 |     // 高效
75 |     r4 := bufio.NewReader(f)
76 |     b4, err := r4.Peek(5)
77 |     check(err)
78 |     fmt.Printf("5 bytes: %s\n", string(b4))
79 | 
80 |     // 最后关闭打开的文件。一般来讲这个方法会在打开文件的时候,
81 |     // 使用defer来延迟关闭
82 |     f.Close()
83 | }
84 | 
85 |

在运行程序之前,你需要创建一个/tmp/dat文件,然后写入一些测试数据。 86 | 运行结果

87 |
hello world
88 | i am jemy
89 | who are you
90 | what do you like
91 | 5 bytes: hello
92 | 2 bytes @ 6: wo
93 | 2 bytes @ 6: wo
94 | 5 bytes: hello
95 | 
96 | 97 | -------------------------------------------------------------------------------- /GolangStudy/tutorial/go_by_example/go_recursion.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Go 递归函数 10 | 11 | 12 |

Go语言支持递归函数,这里是一个经典的斐波拉切数列的列子。

13 |
package main
14 | 
15 | import "fmt"
16 | 
17 | // fact函数不断地调用自身,直到达到基本状态fact(0)
18 | func fact(n int) int {
19 |     if n == 0 {
20 |         return 1
21 |     }
22 |     return n * fact(n-1)
23 | }
24 | 
25 | func main() {
26 |     fmt.Println(fact(7))
27 | }
28 | 
29 |

输出结果为

30 |
5040
31 | 
32 | 33 | -------------------------------------------------------------------------------- /GolangStudy/tutorial/go_by_example/go_regex.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Go 正则表达式 10 | 11 | 12 |

Go内置了对正则表达式的支持,这里是一般的正则表达式常规用法的例子。

13 |
package main
14 | 
15 | import "bytes"
16 | import "fmt"
17 | import "regexp"
18 | 
19 | func main() {
20 | 
21 |     // 测试模式是否匹配字符串,括号里面的意思是
22 |     // 至少有一个a-z之间的字符存在
23 |     match, _ := regexp.MatchString("p([a-z]+)ch", "peach")
24 |     fmt.Println(match)
25 | 
26 |     // 上面我们直接使用了字符串匹配的正则表达式,
27 |     // 但是对于其他的正则匹配任务,你需要使用
28 |     // `Compile`来使用一个优化过的正则对象
29 |     r, _ := regexp.Compile("p([a-z]+)ch")
30 | 
31 |     // 正则结构体对象有很多方法可以使用,比如上面的例子
32 |     // 也可以像下面这么写
33 |     fmt.Println(r.MatchString("peach"))
34 | 
35 |     // 这个方法检测字符串参数是否存在正则所约束的匹配
36 |     fmt.Println(r.FindString("peach punch"))
37 | 
38 |     // 这个方法查找第一次匹配的索引,并返回匹配字符串
39 |     // 的起始索引和结束索引,而不是匹配的字符串
40 |     fmt.Println(r.FindStringIndex("peach punch"))
41 | 
42 |     // 这个方法返回全局匹配的字符串和局部匹配的字符,比如
43 |     // 这里会返回匹配`p([a-z]+)ch`的字符串
44 |     // 和匹配`([a-z]+)`的字符串
45 |     fmt.Println(r.FindStringSubmatch("peach punch"))
46 | 
47 |     // 和上面的方法一样,不同的是返回全局匹配和局部匹配的
48 |     // 起始索引和结束索引
49 |     fmt.Println(r.FindStringSubmatchIndex("peach punch"))
50 | 
51 |     // 这个方法返回所有正则匹配的字符,不仅仅是第一个
52 |     fmt.Println(r.FindAllString("peach punch pinch", -1))
53 | 
54 |     // 这个方法返回所有全局匹配和局部匹配的字符串起始索引
55 |     // 和结束索引
56 |     fmt.Println(r.FindAllStringSubmatchIndex("peach punch pinch", -1))
57 | 
58 |     // 为这个方法提供一个正整数参数来限制匹配数量
59 |     fmt.Println(r.FindAllString("peach punch pinch", 2))
60 | 
61 |     //上面我们都是用了诸如`MatchString`这样的方法,其实
62 |     // 我们也可以使用`[]byte`作为参数,并且使用`Match`
63 |     // 这样的方法名
64 |     fmt.Println(r.Match([]byte("peach")))
65 | 
66 |     // 当使用正则表达式来创建常量的时候,你可以使用`MustCompile`
67 |     // 因为`Compile`返回两个值
68 |     r = regexp.MustCompile("p([a-z]+)ch")
69 |     fmt.Println(r)
70 | 
71 |     // regexp包也可以用来将字符串的一部分替换为其他的值
72 |     fmt.Println(r.ReplaceAllString("a peach", "<fruit>"))
73 | 
74 |     // `Func`变量可以让你将所有匹配的字符串都经过该函数处理
75 |     // 转变为所需要的值
76 |     in := []byte("a peach")
77 |     out := r.ReplaceAllFunc(in, bytes.ToUpper)
78 |     fmt.Println(string(out))
79 | }
80 | 
81 |

运行结果

82 |
true
83 | true
84 | peach
85 | [0 5]
86 | [peach ea]
87 | [0 5 1 3]
88 | [peach punch pinch]
89 | [[0 5 1 3] [6 11 7 9] [12 17 13 15]]
90 | [peach punch]
91 | true
92 | p([a-z]+)ch
93 | a <fruit>
94 | a PEACH
95 | 
96 | 97 | -------------------------------------------------------------------------------- /GolangStudy/tutorial/go_by_example/go_req_freq_control.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Go 请求处理频率控制 10 | 11 | 12 |

频率控制是控制资源利用和保证服务高质量的重要机制。Go可以使用goroutine,channel和ticker来以优雅的方式支持频率控制。

13 |
package main
14 | 
15 | import "time"
16 | import "fmt"
17 | 
18 | func main() {
19 | 
20 |     // 首先我们看下基本的频率限制。假设我们得控制请求频率,
21 |     // 我们使用一个通道来处理所有的这些请求,这里向requests
22 |     // 发送5个数据,然后关闭requests通道
23 |     requests := make(chan int, 5)
24 |     for i := 1; i <= 5; i++ {
25 |         requests <- i
26 |     }
27 |     close(requests)
28 | 
29 |     // 这个limiter的Ticker每隔200毫秒结束通道阻塞
30 |     // 这个limiter就是我们频率控制处理器
31 |     limiter := time.Tick(time.Millisecond * 200)
32 | 
33 |     // 通过阻塞从limiter通道接受数据,我们将请求处理控制在每隔200毫秒
34 |     // 处理一个请求,注意`<-limiter`的阻塞作用。
35 |     for req := range requests {
36 |         <-limiter
37 |         fmt.Println("request", req, time.Now())
38 |     }
39 | 
40 |     // 我们可以保持正常的请求频率限制,但也允许请求短时间内爆发
41 |     // 我们可以通过通道缓存来实现,比如下面的这个burstyLimiter
42 |     // 就允许同时处理3个事件。
43 |     burstyLimiter := make(chan time.Time, 3)
44 | 
45 |     // 填充burstyLimiter,先发送3个数据
46 |     for i := 0; i < 3; i++ {
47 |         burstyLimiter <- time.Now()
48 |     }
49 | 
50 |     // 然后每隔200毫秒再向burstyLimiter发送一个数据,这里是不断地
51 |     // 每隔200毫秒向burstyLimiter发送数据
52 |     go func() {
53 |         for t := range time.Tick(time.Millisecond * 200) {
54 |             burstyLimiter <- t
55 |         }
56 |     }()
57 | 
58 |     // 这里模拟5个请求,burstyRequests的前面3个请求会连续被处理,
59 |     // 因为burstyLimiter被先连续发送3个数据的的缘故,而后面两个
60 |     // 则每隔200毫秒处理一次
61 |     burstyRequests := make(chan int, 5)
62 |     for i := 1; i <= 5; i++ {
63 |         burstyRequests <- i
64 |     }
65 |     close(burstyRequests)
66 |     for req := range burstyRequests {
67 |         <-burstyLimiter
68 |         fmt.Println("request", req, time.Now())
69 |     }
70 | }
71 | 
72 |

运行结果

73 |
request 1 2014-02-21 14:20:05.2696437 +0800 CST
74 | request 2 2014-02-21 14:20:05.4696637 +0800 CST
75 | request 3 2014-02-21 14:20:05.6696837 +0800 CST
76 | request 4 2014-02-21 14:20:05.8697037 +0800 CST
77 | request 5 2014-02-21 14:20:06.0697237 +0800 CST
78 | request 1 2014-02-21 14:20:06.0697237 +0800 CST
79 | request 2 2014-02-21 14:20:06.0697237 +0800 CST
80 | request 3 2014-02-21 14:20:06.0707238 +0800 CST
81 | request 4 2014-02-21 14:20:06.2707438 +0800 CST
82 | request 5 2014-02-21 14:20:06.4707638 +0800 CST
83 | 
84 |

我们从输出的结果上可以看出最后的5个输出结果中,前三个的时间是连续的,而后两个的时间是隔了200毫秒。

85 | 86 | -------------------------------------------------------------------------------- /GolangStudy/tutorial/go_by_example/go_sha1_hash.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Go SHA1 散列 10 | 11 | 12 |

SHA1散列经常用来计算二进制或者大文本数据的短标识值。git版本控制系统用SHA1来标识受版本控制的文件和目录。这里介绍Go中如何计算SHA1散列值。 13 | Go在crypto/*包里面实现了几个常用的散列函数。

14 |
package main
15 | 
16 | import "crypto/sha1"
17 | import "fmt"
18 | 
19 | func main() {
20 |     s := "sha1 this string"
21 | 
22 |     // 生成一个hash的模式是`sha1.New()`,`sha1.Write(bytes)`
23 |     // 然后是`sha1.Sum([]byte{})`,下面我们开始一个新的hash
24 |     // 示例
25 |     h := sha1.New()
26 | 
27 |     // 写入要hash的字节,如果你的参数是字符串,使用`[]byte(s)`
28 |     // 把它强制转换为字节数组
29 |     h.Write([]byte(s))
30 | 
31 |     // 这里计算最终的hash值,Sum的参数是用来追加而外的字节到要
32 |     // 计算的hash字节里面,一般来讲,如果上面已经把需要hash的
33 |     // 字节都写入了,这里就设为nil就可以了
34 |     bs := h.Sum(nil)
35 | 
36 |     // SHA1散列值经常以16进制的方式输出,例如git commit就是
37 |     // 这样,所以可以使用`%x`来将散列结果格式化为16进制的字符串
38 |     fmt.Println(s)
39 |     fmt.Printf("%x\n", bs)
40 | }
41 | 
42 |

运行结果

43 |
sha1 this string
44 | cf23df2207d99a74fbe169e3eba035e633b65d94
45 | 
46 | 47 | -------------------------------------------------------------------------------- /GolangStudy/tutorial/go_by_example/go_signal_handle.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Go 信号处理 10 | 11 | 12 |

有的时候我们希望Go能够智能地处理Unix信号。例如我们希望一个server接收到一个SIGTERM的信号时,能够自动地停止;或者一个命令行工具接收到一个SIGINT信号时,能够停止接收输入。现在我们来看下如何使用channel来处理信号。

13 |
package main
14 | 
15 | import "fmt"
16 | import "os"
17 | import "os/signal"
18 | import "syscall"
19 | 
20 | func main() {
21 | 
22 |     // Go信号通知通过向一个channel发送``os.Signal`来实现。
23 |     // 我们将创建一个channel来接受这些通知,同时我们还用
24 |     // 一个channel来在程序可以退出的时候通知我们
25 |     sigs := make(chan os.Signal, 1)
26 |     done := make(chan bool, 1)
27 | 
28 |     // `signal.Notify`在给定的channel上面注册该channel
29 |     // 可以接受的信号
30 |     signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
31 | 
32 |     // 这个goroutine阻塞等待信号的到来,当信号到来的时候,
33 |     // 输出该信号,然后通知程序可以结束了
34 |     go func() {
35 |         sig := <-sigs
36 |         fmt.Println()
37 |         fmt.Println(sig)
38 |         done <- true
39 |     }()
40 | 
41 |     // 程序将等待接受信号,然后退出
42 |     fmt.Println("awaiting signal")
43 |     <-done
44 |     fmt.Println("exiting")
45 | }
46 | 
47 |

当运行程序的时候,程序将阻塞等待信号的到来,我们可以使用CTRL+C来发送一个SIGINT信号,这样程序就会输出interrupt后退出。

48 |
awaiting signal
49 | 
50 | interrupt
51 | exiting
52 | 
53 | 54 | -------------------------------------------------------------------------------- /GolangStudy/tutorial/go_by_example/go_slice.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Go 切片 10 | 11 | 12 |

切片是Go语言的关键类型之一,它提供了比数组更多的功能。

13 |

示例1:

14 |
package main
 15 | 
 16 | import "fmt"
 17 | 
 18 | func main() {
 19 | 
 20 |     // 和数组不同的是,切片的长度是可变的。
 21 |     // 我们可以使用内置函数make来创建一个长度不为零的切片
 22 |     // 这里我们创建了一个长度为3,存储字符串的切片,切片元素
 23 |     // 默认为零值,对于字符串就是""。
 24 |     s := make([]string, 3)
 25 |     fmt.Println("emp:", s)
 26 | 
 27 |     // 可以使用和数组一样的方法来设置元素值或获取元素值
 28 |     s[0] = "a"
 29 |     s[1] = "b"
 30 |     s[2] = "c"
 31 |     fmt.Println("set:", s)
 32 |     fmt.Println("get:", s[2])
 33 | 
 34 |     // 可以用内置函数len获取切片的长度
 35 |     fmt.Println("len:", len(s))
 36 | 
 37 |     // 切片还拥有一些数组所没有的功能。
 38 |     // 例如我们可以使用内置函数append给切片追加值,然后
 39 |     // 返回一个拥有新切片元素的切片。
 40 |     // 注意append函数不会改变原切片,而是生成了一个新切片,
 41 |     // 我们需要用原来的切片来接收这个新切片
 42 |     s = append(s, "d")
 43 |     s = append(s, "e", "f")
 44 |     fmt.Println("apd:", s)
 45 | 
 46 |     // 另外我们还可以从一个切片拷贝元素到另一个切片
 47 |     // 下面的例子就是创建了一个和切片s长度相同的新切片
 48 |     // 然后使用内置的copy函数来拷贝s的元素到c中。
 49 |     c := make([]string, len(s))
 50 |     copy(c, s)
 51 |     fmt.Println("cpy:", c)
 52 | 
 53 |     // 切片还支持一个取切片的操作 "slice[low:high]"
 54 |     // 获取的新切片包含元素"slice[low]",但是不包含"slice[high]"
 55 |     // 下面的例子就是取一个新切片,元素包括"s[2]","s[3]","s[4]"。
 56 |     l := s[2:5]
 57 |     fmt.Println("sl1:", l)
 58 | 
 59 |     // 如果省略low,默认从0开始,不包括"slice[high]"元素
 60 |     l = s[:5]
 61 |     fmt.Println("sl2:", l)
 62 | 
 63 |     // 如果省略high,默认为len(slice),包括"slice[low]"元素
 64 |     l = s[2:]
 65 |     fmt.Println("sl3:", l)
 66 | 
 67 |     // 我们可以同时声明和初始化一个切片
 68 |     t := []string{"g", "h", "i"}
 69 |     fmt.Println("dcl:", t)
 70 | 
 71 |     // 我们也可以创建多维切片,和数组不同的是,切片元素的长度也是可变的。
 72 |     twoD := make([][]int, 3)
 73 |     for i := 0; i < 3; i++ {
 74 |         innerLen := i + 1
 75 |         twoD[i] = make([]int, innerLen)
 76 |         for j := 0; j < innerLen; j++ {
 77 |             twoD[i][j] = i + j
 78 |         }
 79 |     }
 80 |     fmt.Println("2d: ", twoD)
 81 | }
 82 | 
83 |

输出结果为

84 |
emp: [  ]
 85 | set: [a b c]
 86 | get: c
 87 | len: 3
 88 | apd: [a b c d e f]
 89 | cpy: [a b c d e f]
 90 | sl1: [c d e]
 91 | sl2: [a b c d e]
 92 | sl3: [c d e f]
 93 | dcl: [g h i]
 94 | 2d:  [[0] [1 2] [2 3 4]]
 95 | 
96 |

数组和切片的定义方式的区别在于[]之中是否有固定长度或者推断长度标志符...

97 |

示例2:

98 |
package main
 99 | 
100 | import "fmt"
101 | 
102 | func main() {
103 |     s1 := make([]int, 0)
104 |     test(s1)
105 |     fmt.Println(s1)
106 | }
107 | 
108 | func test(s []int) {
109 |     s = append(s, 3)
110 |     //因为原来分配的空间不够,所以在另外一个地址又重新分配了空间,所以原始地址的数据没有变
111 | }
112 | 
113 |

输出结果为:

114 |
[]
115 | 
116 |

若改为:

117 |
package main
118 | 
119 | import "fmt"
120 | 
121 | func main() {
122 |     s1 := make([]int, 0)
123 |     s1 = test(s1)
124 |     fmt.Println(s1)
125 | }
126 | 
127 | func test(s []int) []int {
128 |     s = append(s, 3)
129 |     return s
130 | }
131 | 
132 |

输出结果为:

133 |
[3]//正确结果
134 | 
135 |

示例3:

136 |

cap是slice的最大容量,append函数添加元素,如果超过原始slice的容量,会重新分配底层数组。

137 |
package main
138 | 
139 | import "fmt"
140 | 
141 | func main() {
142 |     s1 := make([]int, 3, 6)
143 |     fmt.Println("s1= ", s1, len(s1), cap(s1))
144 |     s2 := append(s1, 1, 2, 3)
145 |     fmt.Println("s1= ", s1, len(s1), cap(s1))
146 |     fmt.Println("s2= ", s2, len(s2), cap(s2))
147 |     s3 := append(s2, 4, 5, 6)
148 |     fmt.Println("s1= ", s1, len(s1), cap(s1))
149 |     fmt.Println("s2= ", s2, len(s2), cap(s2))
150 |     fmt.Println("s3= ", s3, len(s3), cap(s3))
151 | 
152 | }
153 | 
154 |

输出结果为:

155 |
s1=  [0 0 0] 3 6
156 | s1=  [0 0 0] 3 6
157 | s2=  [0 0 0 1 2 3] 6 6
158 | s1=  [0 0 0] 3 6
159 | s2=  [0 0 0 1 2 3] 6 6
160 | s3=  [0 0 0 1 2 3 4 5 6] 9 12
161 | 
162 |

示例4:

163 |

指向同一底层数组的slice之间copy时,允许存在重叠。copy数组时,受限于src和dst数组的长度最小值。

164 |
package main
165 | 
166 | import "fmt"
167 | 
168 | func main() {
169 |     s1 := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
170 |     s2 := make([]int, 3, 20)
171 |     var n int
172 |     n = copy(s2, s1)
173 |     fmt.Println(n, s2, len(s2), cap(s2))
174 | 
175 |     s3 := s1[4:6]
176 |     fmt.Println(n, s3, len(s3), cap(s3))
177 | 
178 |     n = copy(s3, s1[1:5])
179 |     fmt.Println(n, s3, len(s3), cap(s3))
180 | }
181 | 
182 |

输出结果:

183 |
3 [0 1 2] 3 20
184 | 3 [4 5] 2 6
185 | 2 [1 2] 2 6
186 | 
187 | 188 | -------------------------------------------------------------------------------- /GolangStudy/tutorial/go_by_example/go_sort.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Go 排序 10 | 11 | 12 |

Go的sort包实现了内置数据类型和用户自定义数据类型的排序功能。我们先看看内置数据类型的排序。

13 |
package main
14 | 
15 | import "fmt"
16 | import "sort"
17 | 
18 | func main() {
19 | 
20 |     // 这些排序方法都是针对内置数据类型的。
21 |     // 这里的排序方法都是就地排序,也就是说排序改变了
22 |     // 切片内容,而不是返回一个新的切片
23 |     strs := []string{"c", "a", "b"}
24 |     sort.Strings(strs)
25 |     fmt.Println("Strings:", strs)
26 | 
27 |     // 对于整型的排序
28 |     ints := []int{7, 2, 4}
29 |     sort.Ints(ints)
30 |     fmt.Println("Ints:   ", ints)
31 | 
32 |     // 我们还可以检测切片是否已经排序好
33 |     s := sort.IntsAreSorted(ints)
34 |     fmt.Println("Sorted: ", s)
35 | }
36 | 
37 |

输出结果

38 |
Strings: [a b c]
39 | Ints:    [2 4 7]
40 | Sorted:  true
41 | 
42 | 43 | -------------------------------------------------------------------------------- /GolangStudy/tutorial/go_by_example/go_stateful_goroutine.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Go 状态协程 10 | 11 | 12 |

在上面的例子中,我们演示了如何通过使用mutex来在多个协程之间共享状态。另外一种方法是使用协程内置的同步机制来实现。这种基于通道的方法和Go的通过消息共享内存,保证每份数据为单独的协程所有的理念是一致的。

13 |
package main
 14 | 
 15 | import (
 16 |     "fmt"
 17 |     "math/rand"
 18 |     "sync/atomic"
 19 |     "time"
 20 | )
 21 | 
 22 | // 在这个例子中,将有一个单独的协程拥有这个状态。这样可以
 23 | // 保证这个数据不会被并行访问所破坏。为了读写这个状态,其
 24 | // 他的协程将向这个协程发送信息并且相应地接受返回信息。
 25 | // 这些`readOp`和`writeOp`结构体封装了这些请求和回复
 26 | type readOp struct {
 27 |     key  int
 28 |     resp chan int
 29 | }
 30 | type writeOp struct {
 31 |     key  int
 32 |     val  int
 33 |     resp chan bool
 34 | }
 35 | 
 36 | func main() {
 37 | 
 38 |     // 我们将计算我们执行了多少次操作
 39 |     var ops int64 = 0
 40 | 
 41 |     // reads和writes通道将被其他协程用来从中读取或写入数据
 42 |     reads := make(chan *readOp)
 43 |     writes := make(chan *writeOp)
 44 | 
 45 |     // 这个是拥有`state`的协程,`state`是一个协程的私有map
 46 |     // 变量。这个协程不断地`select`通道`reads`和`writes`,
 47 |     // 当有请求来临的时候进行回复。一旦有请求,首先执行所
 48 |     // 请求的操作,然后给`resp`通道发送一个表示请求成功的值。
 49 |     go func() {
 50 |         var state = make(map[int]int)
 51 |         for {
 52 |             select {
 53 |             case read := <-reads:
 54 |                 read.resp <- state[read.key]
 55 |             case write := <-writes:
 56 |                 state[write.key] = write.val
 57 |                 write.resp <- true
 58 |             }
 59 |         }
 60 |     }()
 61 | 
 62 |     // 这里启动了100个协程来向拥有状态的协程请求读数据。
 63 |     // 每次读操作都需要创建一个`readOp`,然后发送到`reads`
 64 |     // 通道,然后等待接收请求回复
 65 |     for r := 0; r < 100; r++ {
 66 |         go func() {
 67 |             for {
 68 |                 read := &readOp{
 69 |                     key:  rand.Intn(5),
 70 |                     resp: make(chan int)}
 71 |                 reads <- read
 72 |                 <-read.resp
 73 |                 atomic.AddInt64(&ops, 1)
 74 |             }
 75 |         }()
 76 |     }
 77 | 
 78 |     // 我们开启10个写协程
 79 |     for w := 0; w < 10; w++ {
 80 |         go func() {
 81 |             for {
 82 |                 write := &writeOp{
 83 |                     key:  rand.Intn(5),
 84 |                     val:  rand.Intn(100),
 85 |                     resp: make(chan bool)}
 86 |                 writes <- write
 87 |                 <-write.resp
 88 |                 atomic.AddInt64(&ops, 1)
 89 |             }
 90 |         }()
 91 |     }
 92 | 
 93 |     // 让协程运行1秒钟
 94 |     time.Sleep(time.Second)
 95 | 
 96 |     // 最后输出操作数量ops的值
 97 |     opsFinal := atomic.LoadInt64(&ops)
 98 |     fmt.Println("ops:", opsFinal)
 99 | }
100 | 
101 |

运行结果

102 |
ops: 880578
103 | 
104 |

运行这个程序,我们会看到基于协程的状态管理每秒可以处理800, 000个操作。对于这个例子来讲,基于协程的方法比基于mutex的方法更加复杂一点。当然在某些情况下还是很有用的。例如你有很多复杂的协程,而且管理多个mutex可能导致错误。 105 | 当然你可以选择使用任意一种方法,只要你保证这种方法让你觉得很舒服而且也能保证程序的正确性。

106 | 107 | -------------------------------------------------------------------------------- /GolangStudy/tutorial/go_by_example/go_string_byte_convert.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Go String与Byte切片之间的转换 10 | 11 | 12 |

String转换到Byte数组时,每个byte(byte类型其实就是uint8)保存字符串对应字节的数值。

13 |

注意Go的字符串是UTF-8编码的,每个字符长度是不确定的,一些字符可能是1、2、3或者4个字节结尾。

14 |

示例1:

15 |
package main
16 | 
17 | import "fmt"
18 | 
19 | func main() {
20 | 
21 |     s1 := "abcd"
22 |     b1 := []byte(s1)
23 |     fmt.Println(b1) // [97 98 99 100]
24 | 
25 |     s2 := "中文"
26 |     b2 := []byte(s2)
27 |     fmt.Println(b2)  // [228 184 173 230 150 135], unicode,每个中文字符会由三个byte组成
28 | 
29 |     r1 := []rune(s1)
30 |     fmt.Println(r1) // [97 98 99 100], 每个字一个数值
31 | 
32 |     r2 := []rune(s2)
33 |     fmt.Println(r2) // [20013 25991], 每个字一个数值
34 | 
35 | }
36 | 
37 | 38 | -------------------------------------------------------------------------------- /GolangStudy/tutorial/go_by_example/go_string_format.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Go 字符串格式化 10 | 11 | 12 |

Go对字符串格式化提供了良好的支持。下面我们看些常用的字符串格式化的例子。

13 |
package main
 14 | 
 15 | import "fmt"
 16 | import "os"
 17 | 
 18 | type point struct {
 19 |     x, y int
 20 | }
 21 | 
 22 | func main() {
 23 | 
 24 |     // Go提供了几种打印格式,用来格式化一般的Go值,例如
 25 |     // 下面的%v打印了一个point结构体的对象的值
 26 |     p := point{1, 2}
 27 |     fmt.Printf("%v\n", p)
 28 | 
 29 |     // 如果所格式化的值是一个结构体对象,那么`%+v`的格式化输出
 30 |     // 将包括结构体的成员名称和值
 31 |     fmt.Printf("%+v\n", p)
 32 | 
 33 |     // `%#v`格式化输出将输出一个值的Go语法表示方式。
 34 |     fmt.Printf("%#v\n", p)
 35 | 
 36 |     // 使用`%T`来输出一个值的数据类型
 37 |     fmt.Printf("%T\n", p)
 38 | 
 39 |     // 格式化布尔型变量
 40 |     fmt.Printf("%t\n", true)
 41 | 
 42 |     // 有很多的方式可以格式化整型,使用`%d`是一种
 43 |     // 标准的以10进制来输出整型的方式
 44 |     fmt.Printf("%d\n", 123)
 45 | 
 46 |     // 这种方式输出整型的二进制表示方式
 47 |     fmt.Printf("%b\n", 14)
 48 | 
 49 |     // 这里打印出该整型数值所对应的字符
 50 |     fmt.Printf("%c\n", 33)
 51 | 
 52 |     // 使用`%x`输出一个值的16进制表示方式
 53 |     fmt.Printf("%x\n", 456)
 54 | 
 55 |     // 浮点型数值也有几种格式化方法。最基本的一种是`%f`
 56 |     fmt.Printf("%f\n", 78.9)
 57 | 
 58 |     // `%e`和`%E`使用科学计数法来输出整型
 59 |     fmt.Printf("%e\n", 123400000.0)
 60 |     fmt.Printf("%E\n", 123400000.0)
 61 | 
 62 |     // 使用`%s`输出基本的字符串
 63 |     fmt.Printf("%s\n", "\"string\"")
 64 | 
 65 |     // 输出像Go源码中那样带双引号的字符串,需使用`%q`
 66 |     fmt.Printf("%q\n", "\"string\"")
 67 | 
 68 |     // `%x`以16进制输出字符串,每个字符串的字节用两个字符输出
 69 |     fmt.Printf("%x\n", "hex this")
 70 | 
 71 |     // 使用`%p`输出一个指针的值
 72 |     fmt.Printf("%p\n", &p)
 73 | 
 74 |     // 当输出数字的时候,经常需要去控制输出的宽度和精度。
 75 |     // 可以使用一个位于%后面的数字来控制输出的宽度,默认
 76 |     // 情况下输出是右对齐的,左边加上空格
 77 |     fmt.Printf("|%6d|%6d|\n", 12, 345)
 78 | 
 79 |     // 你也可以指定浮点数的输出宽度,同时你还可以指定浮点数
 80 |     // 的输出精度
 81 |     fmt.Printf("|%6.2f|%6.2f|\n", 1.2, 3.45)
 82 | 
 83 |     // To left-justify, use the `-` flag.
 84 |     fmt.Printf("|%-6.2f|%-6.2f|\n", 1.2, 3.45)
 85 | 
 86 |     // 你也可以指定输出字符串的宽度来保证它们输出对齐。默认
 87 |     // 情况下,输出是右对齐的
 88 |     fmt.Printf("|%6s|%6s|\n", "foo", "b")
 89 | 
 90 |     // 为了使用左对齐你可以在宽度之前加上`-`号
 91 |     fmt.Printf("|%-6s|%-6s|\n", "foo", "b")
 92 | 
 93 |     // `Printf`函数的输出是输出到命令行`os.Stdout`的,你
 94 |     // 可以用`Sprintf`来将格式化后的字符串赋值给一个变量
 95 |     s := fmt.Sprintf("a %s", "string")
 96 |     fmt.Println(s)
 97 | 
 98 |     // 你也可以使用`Fprintf`来将格式化后的值输出到`io.Writers`
 99 |     fmt.Fprintf(os.Stderr, "an %s\n", "error")
100 | }
101 | 
102 |

运行结果

103 |
{1 2}
104 | {x:1 y:2}
105 | main.point{x:1, y:2}
106 | main.point
107 | true
108 | 123
109 | 1110
110 | !
111 | 1c8
112 | 78.900000
113 | 1.234000e+08
114 | 1.234000E+08
115 | "string"
116 | "\"string\""
117 | 6865782074686973
118 | 0x103a10c0
119 | |    12|   345|
120 | |  1.20|  3.45|
121 | |1.20  |3.45  |
122 | |   foo|     b|
123 | |foo   |b     |
124 | a string
125 | an error
126 | 
127 | 128 | -------------------------------------------------------------------------------- /GolangStudy/tutorial/go_by_example/go_string_manipulate.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Go 字符串操作函数 10 | 11 | 12 |

strings 标准库提供了很多字符串操作相关的函数。这里提供的几个例子是让你先对这个包有个基本了解。

13 |
package main
14 | 
15 | import s "strings"
16 | import "fmt"
17 | 
18 | // 这里给fmt.Println起个别名,因为下面我们会多处使用。
19 | var p = fmt.Println
20 | 
21 | func main() {
22 | 
23 |     // 下面是strings包里面提供的一些函数实例。注意这里的函数并不是
24 |     // string对象所拥有的方法,这就是说使用这些字符串操作函数的时候
25 |     // 你必须将字符串对象作为第一个参数传递进去。
26 |     p("Contains:  ", s.Contains("test", "es"))
27 |     p("Count:     ", s.Count("test", "t"))
28 |     p("HasPrefix: ", s.HasPrefix("test", "te"))
29 |     p("HasSuffix: ", s.HasSuffix("test", "st"))
30 |     p("Index:     ", s.Index("test", "e"))
31 |     p("Join:      ", s.Join([]string{"a", "b"}, "-"))
32 |     p("Repeat:    ", s.Repeat("a", 5))
33 |     p("Replace:   ", s.Replace("foo", "o", "0", -1))
34 |     p("Replace:   ", s.Replace("foo", "o", "0", 1))
35 |     p("Split:     ", s.Split("a-b-c-d-e", "-"))
36 |     p("ToLower:   ", s.ToLower("TEST"))
37 |     p("ToUpper:   ", s.ToUpper("test"))
38 |     p()
39 | 
40 |     // 你可以在strings包里面找到更多的函数
41 | 
42 |     // 这里还有两个字符串操作方法,它们虽然不是strings包里面的函数,
43 |     // 但是还是值得提一下。一个是获取字符串长度,另外一个是从字符串中
44 |     // 获取指定索引的字符
45 |     p("Len: ", len("hello"))
46 |     p("Char:", "hello"[1])
47 | }
48 | 
49 |

运行结果

50 |
Contains:   true
51 | Count:      2
52 | HasPrefix:  true
53 | HasSuffix:  true
54 | Index:      1
55 | Join:       a-b
56 | Repeat:     aaaaa
57 | Replace:    f00
58 | Replace:    f0o
59 | Split:      [a b c d e]
60 | ToLower:    test
61 | ToUpper:    TEST
62 | 
63 | Len:  5
64 | Char: 101
65 | 
66 | 67 | -------------------------------------------------------------------------------- /GolangStudy/tutorial/go_by_example/go_struct.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Go 结构体 10 | 11 | 12 |

Go语言结构体数据类是将各个类型的变量定义的集合,通常用来表示记录。

13 |
package main
14 | 
15 | import "fmt"
16 | 
17 | // 这个person结构体有name和age成员
18 | type person struct {
19 |     name string
20 |     age  int
21 | }
22 | 
23 | func main() {
24 | 
25 |     // 这个语法创建一个新结构体变量
26 |     fmt.Println(person{"Bob", 20})
27 | 
28 |     // 可以使用"成员:值"的方式来初始化结构体变量
29 |     fmt.Println(person{name: "Alice", age: 30})
30 | 
31 |     // 未显式赋值的成员初始值为零值
32 |     fmt.Println(person{name: "Fred"})
33 | 
34 |     // 可以使用&来获取结构体变量的地址
35 |     fmt.Println(&person{name: "Ann", age: 40})
36 | 
37 |     // 使用点号(.)来访问结构体成员
38 |     s := person{name: "Sean", age: 50}
39 |     fmt.Println(s.name)
40 | 
41 |     // 结构体指针也可以使用点号(.)来访问结构体成员
42 |     // Go语言会自动识别出来
43 |     sp := &s
44 |     fmt.Println(sp.age)
45 | 
46 |     // 结构体成员变量的值是可以改变的
47 |     sp.age = 51
48 |     fmt.Println(sp.age)
49 | }
50 | 
51 |

输出结果为

52 |
{Bob 20}
53 | {Alice 30}
54 | {Fred 0}
55 | &{Ann 40}
56 | Sean
57 | 50
58 | 51
59 | 
60 | 61 | -------------------------------------------------------------------------------- /GolangStudy/tutorial/go_by_example/go_switch.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Go switch语句 10 | 11 | 12 |

当条件判断分支太多的时候,我们会使用switch语句来优化逻辑。

13 |
package main
14 | 
15 | import "fmt"
16 | import "time"
17 | 
18 | func main() {
19 | 
20 |     // 基础的switch用法
21 |     i := 2
22 |     fmt.Print("write ", i, " as ")
23 |     switch i {
24 |     case 1:
25 |         fmt.Println("one")
26 |     case 2:
27 |         fmt.Println("two")
28 |     case 3:
29 |         fmt.Println("three")
30 |     }
31 | 
32 |     // 你可以使用逗号来在case中分开多个条件。还可以使用default语句,
33 |     // 当上面的case都没有满足的时候执行default所指定的逻辑块。
34 |     switch time.Now().Weekday() {
35 |     case time.Saturday, time.Sunday:
36 |         fmt.Println("it's the weekend")
37 |     default:
38 |         fmt.Println("it's a weekday")
39 |     }
40 | 
41 |     // 当switch没有跟表达式的时候,功能和if/else相同,这里我们
42 |     // 还可以看到case后面的表达式不一定是常量。
43 |     t := time.Now()
44 |     switch {
45 |     case t.Hour() < 12:
46 |         fmt.Println("it's before noon")
47 |     default:
48 |         fmt.Println("it's after noon")
49 |     }
50 | }
51 | 
52 |

输出结果为

53 |
write 2 as two
54 | it's a weekday
55 | it's before noon
56 | 
57 | 58 | -------------------------------------------------------------------------------- /GolangStudy/tutorial/go_by_example/go_ticker.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Go 打点器 10 | 11 | 12 |

Timer是让你等待一段时间然后去做一件事情,这件事情只会做一次。而Ticker是让你按照一定的时间间隔循环往复地做一件事情,除非你手动停止它。

13 |
package main
14 | 
15 | import "time"
16 | import "fmt"
17 | 
18 | func main() {
19 | 
20 |     // Ticker使用和Timer相似的机制,同样是使用一个通道来发送数据。
21 |     // 这里我们使用range函数来遍历通道数据,这些数据每隔500毫秒被
22 |     // 发送一次,这样我们就可以接收到
23 |     ticker := time.NewTicker(time.Millisecond * 500)
24 |     go func() {
25 |         for t := range ticker.C {
26 |             fmt.Println("Tick at", t)
27 |         }
28 |     }()
29 | 
30 |     // Ticker和Timer一样可以被停止。一旦Ticker停止后,通道将不再
31 |     // 接收数据,这里我们将在1500毫秒之后停止
32 |     time.Sleep(time.Millisecond * 1500)
33 |     ticker.Stop()
34 |     fmt.Println("Ticker stopped")
35 | }
36 | 
37 |

输出结果

38 |
Tick at 2014-02-18 05:42:50.363640783 +0800 CST
39 | Tick at 2014-02-18 05:42:50.863793985 +0800 CST
40 | Tick at 2014-02-18 05:42:51.363532887 +0800 CST
41 | Ticker stopped
42 | 
43 |

在这个例子中,我们让Ticker一个独立协程上每隔500毫秒执行一次,然后在main函数所在协程上等待1500毫秒,然后停止Ticker。所以只输出了3次Ticker at信息。

44 | 45 | -------------------------------------------------------------------------------- /GolangStudy/tutorial/go_by_example/go_time.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Go 时间 10 | 11 | 12 |

Go提供了对时间和一段时间的支持。这里有一些例子。

13 |
package main
14 | 
15 | import "fmt"
16 | import "time"
17 | 
18 | func main() {
19 | 
20 |     // 从获取当前时间开始
21 |     var now = time.Now()
22 |     fmt.Println(now)
23 | 
24 |     // 你可以提供年,月,日等来创建一个时间。当然时间
25 |     // 总是会和地区联系在一起,也就是时区
26 |     var then = time.Date(2009, 11, 17, 20, 34, 58, 651387237, time.UTC)
27 |     fmt.Println(then)
28 | 
29 |     // 你可以获取时间的各个组成部分
30 |     fmt.Println(then.Year())
31 |     fmt.Println(then.Month())
32 |     fmt.Println(then.Day())
33 |     fmt.Println(then.Hour())
34 |     fmt.Println(then.Minute())
35 |     fmt.Println(then.Second())
36 |     fmt.Println(then.Nanosecond())
37 |     fmt.Println(then.Location())
38 | 
39 |     // 输出当天是周几,Monday-Sunday中的一个
40 |     fmt.Println(then.Weekday())
41 | 
42 |     // 下面的几个方法判断两个时间的顺序,精确到秒
43 |     fmt.Println(then.Before(now))
44 |     fmt.Println(then.After(now))
45 |     fmt.Println(then.Equal(now))
46 | 
47 |     // Sub方法返回两个时间的间隔(Duration)
48 |     var diff = now.Sub(then)
49 |     fmt.Println(diff)
50 | 
51 |     // 可以以不同的单位来计算间隔的大小
52 |     fmt.Println(diff.Hours())
53 |     fmt.Println(diff.Minutes())
54 |     fmt.Println(diff.Seconds())
55 |     fmt.Println(diff.Nanoseconds())
56 | 
57 |     // 你可以使用Add方法来为时间增加一个间隔
58 |     // 使用负号表示时间向前推移一个时间间隔
59 |     fmt.Println(then.Add(diff))
60 |     fmt.Println(then.Add(-diff))
61 | }
62 | 
63 |

运行结果

64 |
2014-03-02 22:54:40.561698065 +0800 CST
65 | 2009-11-17 20:34:58.651387237 +0000 UTC
66 | 2009
67 | November
68 | 17
69 | 20
70 | 34
71 | 58
72 | 651387237
73 | UTC
74 | Tuesday
75 | true
76 | false
77 | false
78 | 37578h19m41.910310828s
79 | 37578.328308419674
80 | 2.2546996985051804e+06
81 | 1.3528198191031083e+08
82 | 135281981910310828
83 | 2014-03-02 14:54:40.561698065 +0000 UTC
84 | 2005-08-05 02:15:16.741076409 +0000 UTC
85 | 
86 | 87 | -------------------------------------------------------------------------------- /GolangStudy/tutorial/go_by_example/go_time_format_parse.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Go 时间格式化和解析 10 | 11 | 12 |

Go使用模式匹配的方式来支持日期格式化和解析。

13 |
package main
14 | 
15 | import "fmt"
16 | import "time"
17 | 
18 | func main() {
19 |     // 这里有一个根据RFC3339来格式化日期的例子
20 |     t := time.Now()
21 |     fmt.Println(t.Format("2006-01-02T15:04:05Z07:00"))
22 | 
23 |     // Format 函数使用一种基于示例的模式匹配方式,
24 |     // 它使用已经格式化的时间模式来决定所给定参数
25 |     // 的输出格式
26 |     fmt.Println(t.Format("3:04PM"))
27 |     fmt.Println(t.Format("Mon Jan _2 15:04:05 2006"))
28 |     fmt.Println(t.Format("2006-01-02T15:04:05.999999-07:00"))
29 | 
30 |     // 对于纯数字表示的时间来讲,你也可以使用标准
31 |     // 的格式化字符串的方式来格式化时间
32 |     fmt.Printf("%d-%02d-%02dT%02d:%02d:%02d-00:00\n",
33 |         t.Year(), t.Month(), t.Day(),
34 |         t.Hour(), t.Minute(), t.Second())
35 | 
36 |     // 时间解析也是采用一样的基于示例的方式
37 |     withNanos := "2006-01-02T15:04:05.999999999-07:00"
38 |     t1, e := time.Parse(
39 |         withNanos,
40 |         "2012-11-01T22:08:41.117442+00:00")
41 |     fmt.Println(t1)
42 |     kitchen := "3:04PM"
43 |     t2, e := time.Parse(kitchen, "8:41PM")
44 |     fmt.Println(t2)
45 | 
46 |     // Parse将返回一个错误,如果所输入的时间格式不对的话
47 |     ansic := "Mon Jan _2 15:04:05 2006"
48 |     _, e = time.Parse(ansic, "8:41PM")
49 |     fmt.Println(e)
50 | 
51 |     // 你可以使用一些预定义的格式来格式化或解析时间
52 |     fmt.Println(t.Format(time.Kitchen))
53 | }
54 | 
55 |

运行结果

56 |
2014-03-03T22:39:31+08:00
57 | 10:39PM
58 | Mon Mar  3 22:39:31 2014
59 | 2014-03-03T22:39:31.647077+08:00
60 | 2014-03-03T22:39:31-00:00
61 | 2012-11-01 22:08:41.117442 +0000 +0000
62 | 0000-01-01 20:41:00 +0000 UTC
63 | parsing time "8:41PM" as "Mon Jan _2 15:04:05 2006": cannot parse "8:41PM" as "Mon"
64 | 10:39PM
65 | 
66 | 67 | -------------------------------------------------------------------------------- /GolangStudy/tutorial/go_by_example/go_timeout.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Go 超时 10 | 11 | 12 |

超时对那些连接外部资源的程序来说是很重要的,否则就需要限定执行时间。在Go里面实现超时很简单。我们可以使用channel和select很容易地做到。

13 |
package main
14 | 
15 | import "time"
16 | import "fmt"
17 | 
18 | func main() {
19 | 
20 |     // 在这个例子中,假设我们执行了一个外部调用,2秒之后将结果写入c1
21 |     c1 := make(chan string, 1)
22 |     go func() {
23 |         time.Sleep(time.Second * 2)
24 |         c1 <- "result 1"
25 |     }()
26 | 
27 |     // 这里使用select来实现超时,`res := <-c1`等待通道结果,
28 |     // `<- Time.After`则在等待1秒后返回一个值,因为select首先
29 |     // 执行那些不再阻塞的case,所以这里会执行超时程序,如果
30 |     // `res := <-c1`超过1秒没有执行的话
31 |     select {
32 |     case res := <-c1:
33 |         fmt.Println(res)
34 |     case <-time.After(time.Second * 1):
35 |         fmt.Println("timeout 1")
36 |     }
37 | 
38 |     // 如果我们将超时时间设为3秒,这个时候`res := <-c2`将在
39 |     // 超时case之前执行,从而能够输出写入通道c2的值
40 |     c2 := make(chan string, 1)
41 |     go func() {
42 |         time.Sleep(time.Second * 2)
43 |         c2 <- "result 2"
44 |     }()
45 |     select {
46 |     case res := <-c2:
47 |         fmt.Println(res)
48 |     case <-time.After(time.Second * 3):
49 |         fmt.Println("timeout 2")
50 |     }
51 | }
52 | 
53 |

运行结果

54 |
timeout 1
55 | result 2
56 | 
57 | 58 | -------------------------------------------------------------------------------- /GolangStudy/tutorial/go_by_example/go_timer.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Go 计时器 10 | 11 | 12 |

我们有的时候希望Go在未来的某个时刻执行或者是以一定的时间间隔重复执行。Go内置的timer和ticker功能使得这些任务变得简单了。我们先看看timer的功能,下一节再看看ticker的功能。

13 |
package main
14 | 
15 | import "time"
16 | import "fmt"
17 | 
18 | func main() {
19 |     // Timer 代表了未来的一个事件,你告诉timer需要等待多久,然后
20 |     // 计时器提供了一个通道,这个通道将在等待的时间结束后得到通知,
21 |     // 这里的timer将等待2秒
22 |     timer1 := time.NewTimer(time.Second * 2)
23 | 
24 |     // 这里`<-timer1.C`在timer的通道`C`上面阻塞等待,直到有个值发送给该
25 |     // 通道,通知通道计时器已经等待完成。
26 |     // timer.NewTimer方法获取的timer1的结构体定义为
27 |     // type Ticket struct{
28 |     //  C <-chan Time
29 |     //}
30 |     <-timer1.C
31 |     fmt.Println("Timer 1 expired")
32 | 
33 |     // 如果你仅仅需要等待的话,你可以使用`time.Sleep`,而timer的
34 |     // 独特之处在于你可以在timer等待完成之前取消等待。
35 |     timer2 := time.NewTimer(time.Second)
36 |     go func() {
37 |         <-timer2.C
38 |         fmt.Println("Timer 2 expired")
39 |     }()
40 |     stop2 := timer2.Stop()
41 |     if stop2 {
42 |         fmt.Println("Timer 2 stopped")
43 |     }
44 | }
45 | 
46 |

运行结果

47 |
Timer 1 expired
48 | Timer 2 stopped
49 | 
50 |

在上面的例子中,第一个timer将在2秒后等待完成而第二个timer则在等待完成之前被取消了。

51 | 52 | -------------------------------------------------------------------------------- /GolangStudy/tutorial/go_by_example/go_timestamp.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Go 时间戳 10 | 11 | 12 |

程序的一个通常需求是计算从Unix起始时间开始到某个时刻的秒数,毫秒数,微秒数等。 13 | 我们来看看Go里面是怎么做的。

14 |
package main
15 | 
16 | import "fmt"
17 | import "time"
18 | 
19 | func main() {
20 | 
21 |     // 使用Unix和UnixNano来分别获取从Unix起始时间
22 |     // 到现在所经过的秒数和微秒数
23 |     now := time.Now()
24 |     secs := now.Unix()
25 |     nanos := now.UnixNano()
26 |     fmt.Println(now)
27 | 
28 |     // 注意这里没有UnixMillis方法,所以我们需要将
29 |     // 微秒手动除以一个数值来获取毫秒
30 |     millis := nanos / 1000000
31 |     fmt.Println(secs)
32 |     fmt.Println(millis)
33 |     fmt.Println(nanos)
34 | 
35 |     // 反过来,你也可以将一个整数秒数或者微秒数转换
36 |     // 为对应的时间
37 |     fmt.Println(time.Unix(secs, 0))
38 |     fmt.Println(time.Unix(0, nanos))
39 | }
40 | 
41 |

运行结果

42 |
2014-03-02 23:11:31.118666918 +0800 CST
43 | 1393773091
44 | 1393773091118
45 | 1393773091118666918
46 | 2014-03-02 23:11:31 +0800 CST
47 | 2014-03-02 23:11:31.118666918 +0800 CST
48 | 
49 | 50 | -------------------------------------------------------------------------------- /GolangStudy/tutorial/go_by_example/go_url_parse.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Go URL解析 10 | 11 | 12 |

URL提供了一种统一访问资源的方式。我们来看一下Go里面如何解析URL。

13 |
package main
14 | 
15 | import "fmt"
16 | import "net/url"
17 | import "strings"
18 | 
19 | func main() {
20 | 
21 |     // 我们将解析这个URL,它包含了模式,验证信息,
22 |     // 主机,端口,路径,查询参数和查询片段
23 |     s := "postgres://user:pass@host.com:5432/path?k=v#f"
24 | 
25 |     // 解析URL,并保证没有错误
26 |     u, err := url.Parse(s)
27 |     if err != nil {
28 |         panic(err)
29 |     }
30 | 
31 |     // 可以直接访问解析后的模式
32 |     fmt.Println(u.Scheme)
33 | 
34 |     // User包含了所有的验证信息,使用
35 |     // Username和Password来获取单独的信息
36 |     fmt.Println(u.User)
37 |     fmt.Println(u.User.Username())
38 |     p, _ := u.User.Password()
39 |     fmt.Println(p)
40 | 
41 |     // Host包含了主机名和端口,如果需要可以
42 |     // 手动分解主机名和端口
43 |     fmt.Println(u.Host)
44 |     h := strings.Split(u.Host, ":")
45 |     fmt.Println(h[0])
46 |     fmt.Println(h[1])
47 | 
48 |     // 这里我们解析出路径和`#`后面的片段
49 |     fmt.Println(u.Path)
50 |     fmt.Println(u.Fragment)
51 | 
52 |     // 为了得到`k=v`格式的查询参数,使用RawQuery。你可以将
53 |     // 查询参数解析到一个map里面。这个map为字符串作为key,
54 |     // 字符串切片作为value。
55 |     fmt.Println(u.RawQuery)
56 |     m, _ := url.ParseQuery(u.RawQuery)
57 |     fmt.Println(m)
58 |     fmt.Println(m["k"][0])
59 | }
60 | 
61 |

运行结果

62 |
postgres
63 | user:pass
64 | user
65 | pass
66 | host.com:5432
67 | host.com
68 | 5432
69 | /path
70 | f
71 | k=v
72 | map[k:[v]]
73 | v
74 | 
75 | 76 | -------------------------------------------------------------------------------- /GolangStudy/tutorial/go_by_example/go_variable.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Go 变量 10 | 11 | 12 |

Go是静态类型语言,变量是有明确类型的。编译器会检查函数调用中,变量类型的正确性。

13 |

使用var关键字来定义变量。

14 |

Go 的基本类型有:

15 | 25 |

看看下面的例子

26 |
package main
27 | 
28 | import "fmt"
29 | 
30 | func main() {
31 |     // `var` 关键字用来定义一个或者多个变量
32 |     var a string = "initial"
33 |     fmt.Println(a)
34 | 
35 |     // 你一次可以定义多个变量
36 |     var b, c int = 1, 2
37 |     fmt.Println(b, c)
38 | 
39 |     // Go会推断出具有初始值的变量的类型
40 |     var d = true
41 |     fmt.Println(d)
42 | 
43 |     //定义变量时,没有给出初始值的变量被默认初始化为零值
44 |     //整型的零值就是0
45 |     var e int
46 |     fmt.Println(e)
47 | 
48 |     //":=" 语法是同时定义和初始化变量的快捷方式
49 |     f := "short"
50 |     fmt.Println(f)
51 | }
52 | 
53 |

输出结果为

54 |
initial
55 | 1 2
56 | true
57 | 0
58 | short
59 | 
60 | 61 | -------------------------------------------------------------------------------- /GolangStudy/tutorial/go_by_example/go_variable_argument_list.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Go 可变长参数列表 10 | 11 | 12 |

支持可变长参数列表的函数可以支持任意个传入参数,比如fmt.Println函数就是一个支持可变长参数列表的函数。

13 |
package main
14 | 
15 | import "fmt"
16 | 
17 | // 这个函数可以传入任意数量的整型参数
18 | func sum(nums ...int) {
19 |     fmt.Print(nums, " ")
20 |     total := 0
21 |     for _, num := range nums {
22 |         total += num
23 |     }
24 |     fmt.Println(total)
25 | }
26 | 
27 | func main() {
28 | 
29 |     // 支持可变长参数的函数调用方法和普通函数一样
30 |     // 也支持只有一个参数的情况
31 |     sum(1, 2)
32 |     sum(1, 2, 3)
33 | 
34 |     // 如果你需要传入的参数在一个切片中,像下面一样
35 |     // "func(slice...)"把切片打散传入
36 |     nums := []int{1, 2, 3, 4}
37 |     sum(nums...)
38 | }
39 | 
40 |

输出结果为

41 |
[1 2] 3
42 | [1 2 3] 6
43 | [1 2 3 4] 10
44 | 
45 |

需要注意的是,可变长参数应该是函数定义的最右边的参数,即最后一个参数。

46 | 47 | -------------------------------------------------------------------------------- /GolangStudy/tutorial/go_by_example/go_working_pool.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Go 工作池 10 | 11 | 12 |

在这个例子中,我们来看一下如何使用gorouotine和channel来实现工作池。

13 |
package main
14 | 
15 | import "fmt"
16 | import "time"
17 | 
18 | // 我们将在worker函数里面运行几个并行实例,这个函数从jobs通道
19 | // 里面接受任务,然后把运行结果发送到results通道。每个job我们
20 | // 都休眠一会儿,来模拟一个耗时任务。
21 | func worker(id int, jobs <-chan int, results chan<- int) {
22 |     for j := range jobs {
23 |         fmt.Println("worker", id, "processing job", j)
24 |         time.Sleep(time.Second)
25 |         results <- j * 2
26 |     }
27 | }
28 | 
29 | func main() {
30 | 
31 |     // 为了使用我们的工作池,我们需要发送工作和接受工作的结果,
32 |     // 这里我们定义两个通道,一个jobs,一个results
33 |     jobs := make(chan int, 100)
34 |     results := make(chan int, 100)
35 | 
36 |     // 这里启动3个worker协程,一开始的时候worker阻塞执行,因为
37 |     // jobs通道里面还没有工作任务
38 |     for w := 1; w <= 3; w++ {
39 |         go worker(w, jobs, results)
40 |     }
41 | 
42 |     // 这里我们发送9个任务,然后关闭通道,告知任务发送完成
43 |     for j := 1; j <= 9; j++ {
44 |         jobs <- j
45 |     }
46 |     close(jobs)
47 | 
48 |     // 然后我们从results里面获得结果
49 |     for a := 1; a <= 9; a++ {
50 |         <-results
51 |     }
52 | 
53 |

运行结果

54 |
worker 1 processing job 1
55 | worker 2 processing job 2
56 | worker 3 processing job 3
57 | worker 1 processing job 4
58 | worker 3 processing job 5
59 | worker 2 processing job 6
60 | worker 1 processing job 7
61 | worker 3 processing job 8
62 | worker 2 processing job 9
63 | 
64 | 65 | -------------------------------------------------------------------------------- /GolangStudy/tutorial/go_by_example/go_write_file.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Go 写入文件 10 | 11 | 12 |

Go将数据写入文件的方法和上面介绍过的读取文件的方法很类似。

13 |
package main
14 | 
15 | import (
16 |     "bufio"
17 |     "fmt"
18 |     "io/ioutil"
19 |     "os"
20 | )
21 | 
22 | func check(e error) {
23 |     if e != nil {
24 |         panic(e)
25 |     }
26 | }
27 | 
28 | func main() {
29 | 
30 |     // 首先看一下如何将一个字符串写入文件
31 |     d1 := []byte("hello\ngo\n")
32 |     err := ioutil.WriteFile("/tmp/dat1", d1, 0644)
33 |     check(err)
34 | 
35 |     // 为了实现细颗粒度的写入,打开文件后再写入
36 |     f, err := os.Create("/tmp/dat2")
37 |     check(err)
38 | 
39 |     // 在打开文件后通常应该立刻使用defer来调用
40 |     // 打开文件的Close方法,以保证main函数结束
41 |     // 后,文件关闭
42 |     defer f.Close()
43 | 
44 |     // 你可以写入字节切片
45 |     d2 := []byte{115, 111, 109, 101, 10}
46 |     n2, err := f.Write(d2)
47 |     check(err)
48 |     fmt.Printf("wrote %d bytes\n", n2)
49 | 
50 |     // 也可以使用`WriteString`直接写入字符串
51 |     n3, err := f.WriteString("writes\n")
52 |     fmt.Printf("wrote %d bytes\n", n3)
53 | 
54 |     // 调用Sync方法来将缓冲区数据写入磁盘
55 |     f.Sync()
56 | 
57 |     // `bufio`除了提供上面的缓冲读取数据外,还
58 |     // 提供了缓冲写入数据的方法
59 |     w := bufio.NewWriter(f)
60 |     n4, err := w.WriteString("buffered\n")
61 |     fmt.Printf("wrote %d bytes\n", n4)
62 | 
63 |     // 使用Flush方法确保所有缓冲区的数据写入底层writer
64 |     w.Flush()
65 | }
66 | 
67 |

运行结果

68 |
wrote 5 bytes
69 | wrote 7 bytes
70 | wrote 9 bytes
71 | 
72 | 73 | -------------------------------------------------------------------------------- /GolangStudy/tutorial/go_tutorial/default.css: -------------------------------------------------------------------------------- 1 | h1,h2,h3,h4,h5,h6,p,blockquote{margin:0;padding:0}body{font-family:"Helvetica Neue",Helvetica,"Hiragino Sans GB",Arial,sans-serif;font-size:14px;line-height:18px;color:#737373;background-color:white;margin:10px 13px 10px 13px}table{margin:10px 0 15px 0;border-collapse:collapse}td,th{border:1px solid #ddd;padding:3px 10px}th{padding:5px 10px}a{color:#0069d6}a:hover{color:#0050a3;text-decoration:none}a img{border:0}p{margin-bottom:9px}h1,h2,h3,h4,h5,h6{color:#404040;line-height:36px}h1{margin-bottom:18px;font-size:30px}h2{font-size:24px}h3{font-size:18px}h4{font-size:16px}h5{font-size:14px}h6{font-size:13px}hr{margin:0 0 19px;border:0;border-bottom:1px solid #ccc}blockquote{padding:13px 13px 21px 15px;margin-bottom:18px;font-family:georgia,serif;font-style:italic}blockquote:before{content:"\201C";font-size:40px;margin-left:-10px;font-family:georgia,serif;color:#eee}blockquote p{font-size:14px;font-weight:300;line-height:18px;margin-bottom:0;font-style:italic}code,pre{font-family:Monaco,Andale Mono,Courier New,monospace}code{background-color:#fee9cc;color:rgba(0,0,0,0.75);padding:1px 3px;font-size:12px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}pre{display:block;padding:5px;line-height:16px;font-size:11px;white-space:pre-wrap;word-wrap:break-word;border:1px solid #23241f;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}pre code{color:#737373;font-size:11px;padding:0}sup{font-size:.83em;vertical-align:super;line-height:0}*{-webkit-print-color-adjust:exact}@media screen and (min-width:914px){body{width:854px;margin:10px auto}}@media print{body,code,pre code,h1,h2,h3,h4,h5,h6{color:black}table,pre{page-break-inside:avoid}}pre{background:#23241f}pre code{display:block;background:#23241f;padding:0}pre .tag,pre code{color:#f8f8f2}pre .keyword,pre .function,pre .literal,pre .change,pre .winutils,pre .flow,pre .lisp .title,pre .clojure .built_in,pre .nginx .title,pre .tex .special{color:#66d9ef}pre .variable,pre .params{color:#fd9720}pre .constant{color:#66d9ef}pre .title,pre .class .title,pre .css .class{color:#a6e22e}pre .attribute,pre .symbol,pre .symbol .string,pre .tag .title,pre .value,pre .css .tag{color:#f92672}pre .number,pre .preprocessor,pre .pragma,pre .regexp{color:#ae81ff}pre .tag .value,pre .string,pre .css .id,pre .subst,pre .haskell .type,pre .ruby .class .parent,pre .built_in,pre .sql .aggregate,pre .django .template_tag,pre .django .variable,pre .smalltalk .class,pre .django .filter .argument,pre .smalltalk .localvars,pre .smalltalk .array,pre .attr_selector,pre .pseudo,pre .addition,pre .stream,pre .envvar,pre .apache .tag,pre .apache .cbracket,pre .tex .command,pre .prompt{color:#e6db74}pre .comment,pre .javadoc,pre .java .annotation,pre .python .decorator,pre .template_comment,pre .pi,pre .doctype,pre .deletion,pre .shebang,pre .apache .sqbracket,pre .tex .formula{color:#a6e22d;}pre .coffeescript .javascript,pre .javascript .xml,pre .tex .formula{opacity:.5}pre .xml .javascript,pre .xml .vbscript,pre .xml .css,pre .xml .cdata{opacity:.5} -------------------------------------------------------------------------------- /GolangStudy/tutorial/go_tutorial/go_tutorial_10_use_package_test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 使用包和测试管理项目 10 | 11 | 12 | 13 |

Go天生就是为了支持良好的项目管理体验而设计的。

14 | 15 |

16 | 17 |

在软件工程的实践中,我们会遇到很多功能重复的代码,比如去除字符串首尾的空格。高质量软件产品的特点就是它的部分代码是可以重用的,比如你不必每次写个函数去去除字符串首尾的空格。

18 | 19 |

我们上面讲过变量,结构体,接口和函数等,事实上所谓的包,就是把一些用的多的这些变量,结构体,接口和函数等统一放置在一个逻辑块中。并且给它们起一个名字,这个名字就叫做包名。

20 | 21 |

例如我们上面用的最多的fmt包,这个包提供了很多格式化输出的函数,你可以在自己的代码中引用这个包,来做格式化输出,而不用你自己每次去写个函数。一门成熟的语言都会提供齐全的基础功能包供人调用。

22 | 23 |

使用包有三个好处

24 | 25 |
    26 |
  1. 可以减少函数名称重复,因为不同包中可以存在名称相同的函数。否则得话,你得给这些函数加上前缀或者后缀以示区别。
  2. 27 |
  3. 包把函数等组织在一起,方便你查找和重用。比如你想用Println()函数输出一行字符串,你可以很方便地知道它在fmt包中,直接引用过来用就可以了。
  4. 28 |
  5. 使用包可以加速程序编译。因为包是预编译好的,你改动自己代码得时候,不必每次去把包编译一下。
  6. 29 |
30 | 31 | 32 |

创建包

33 | 34 |

我们现在来举个例子,用来演示Go的项目管理。

35 | 36 |

首先我们在目录/Users/jemy/JemyGraw/GoLang下面创建文件夹pkg_demo。然后在pkg_demo里面创建src文件夹 37 | 。然后再在main文件夹里面创建main.go文件。另外为了演示包的创建,我们在src目录下面创建文件夹net.duokr,然后再在net.duokr文件夹里面创建math文件夹,这个文件夹名称就是这个文件夹下面go文件的包名称。然后我们再创建一个math_pkg.go文件,之所以取这个名字而不是math.go只是为了说明这个文件名称和包名不需要一致。然后我们还创建了一个math_pkg_test.go文件作为包的测试用例文件。整体结构如下:

38 | 39 |
.
 40 | └── src
 41 |     ├── main
 42 |     │   ├── build.sh
 43 |     │   └── main.go
 44 |     └── net.duokr
 45 |         └── math
 46 |             ├── math_pkg.go
 47 |             └── math_pkg_test.go
 48 | 
49 | 50 |

其中build.sh是我们为了编译这个项目而写的脚本,因为编译项目需要几条命令,把它写在脚本文件中方便使用。另外为了能够让build.sh能够执行,使用chmod +x build.sh为它赋予可执行权限。build.bat是Windows下面的编译脚本。 51 | 我们来看一下math_pkg.go的定义:

52 | 53 |
package math
 54 | 
 55 | func Add(a, b int) int {
 56 |     return a + b
 57 | }
 58 | func Subtract(a, b int) int {
 59 |     return a - b
 60 | }
 61 | func Multiply(a, b int) int {
 62 |     return a * b
 63 | }
 64 | 
 65 | func Divide(a, b int) int {
 66 |     if b == 0 {
 67 |         panic("Can not divided by zero")
 68 |     }
 69 |     return a / b
 70 | }
 71 | 
72 | 73 |

首先是包名,然后是几个函数定义,这里我们会发现这些函数定义首字母都是大写Go规定了只有首字母大写的函数才能从包导出使用,即其他调用这个包中函数的代码只能调用那些导出的函数

74 | 75 |

我们再看一下main.go的定义:

76 | 77 |
package main
 78 | 
 79 | import (
 80 |     "fmt"
 81 |     math "net.duokr/math"
 82 | )
 83 | 
 84 | func main() {
 85 |     var a = 100
 86 |     var b = 200
 87 | 
 88 |     fmt.Println("Add demo:", math.Add(a, b))
 89 |     fmt.Println("Substract demo:", math.Subtract(a, b))
 90 |     fmt.Println("Multiply demo:", math.Multiply(a, b))
 91 |     fmt.Println("Divide demo:", math.Divide(a, b))
 92 | }
 93 | 
94 | 95 |

在main.go里面,我们使用import关键字引用我们自定义的包math,引用的方法是从main包平行的文件夹net.duokr开始,后面跟上包名math。这里面我们给这个长长的包名起了一个别名就叫math。然后分别调用math包里面的函数。

96 | 97 |

最后我们看一下我们的编译脚本:

98 | 99 |
export GOPATH=$GOPATH:/Users/jemy/JemyGraw/GoLang/pkg_demo
100 | export GOBIN=/Users/jemy/JemyGraw/GoLang/pkg_demo/bin
101 | go build net.duokr/math
102 | go build main.go
103 | go install main
104 | 
105 | 106 |

第一行,我们将项目路径加入GOPATH中,这样待会儿编译main.go的时候才能找到我们自定义的包;

107 | 108 |

第二行,我们设置本项目的安装目录,第五行的命令将编译好的文件放到这个目录下面;

109 | 110 |

第三行,我们编译我们的自定义包;

111 | 112 |

第四行,我们编译我们main.go文件;

113 | 114 |

第五行,将编译好的文件安装到指定目录下。

115 | 116 |

这里还有一个Windows下面的编译脚本build.bat:

117 | 118 |
@echo off
119 | set GOPATH=GOPATH;C:\JemyGraw\GoLang\pkg_demo
120 | set GOBIN=C:\JemyGraw\GoLang\pkg_demo\bin
121 | go build net.duokr\math
122 | go build main.go
123 | go install main
124 | 
125 | 126 |

好了,运行脚本编译一下,在main文件夹和bin文件夹下面都会生成一个可执行文件。

127 | 128 |

这个时候文件夹结构为:

129 | 130 |
.
131 | ├── bin
132 | │   └── main
133 | ├── pkg
134 | │   └── darwin_386
135 | │       └── net.duokr
136 | │           └── math.a
137 | └── src
138 |     ├── main
139 |     │   ├── build.bat
140 |     │   ├── build.sh
141 |     │   ├── main
142 |     │   └── main.go
143 |     └── net.duokr
144 |         └── math
145 |             ├── math_pkg.go
146 |             └── math_pkg_test.go
147 | 
148 | 149 |

运行一下,输出结果为:

150 | 151 |
Add demo: 300
152 | Substract demo: -100
153 | Multiply demo: 20000
154 | Divide demo: 0
155 | 
156 | 157 |

好了,包的使用介绍完毕,我们再来看一下测试用例怎么写。

158 | 159 |

测试

160 | 161 |

在上面的例子中,我们发现我们自定义的包下面还有一个math_pkg_test.go文件,这个文件包含了本包的一些测试用例。而且Go会把以_test.go结尾的文件当作是测试文件。

162 | 163 |

测试怎么写,当然是用assert来判断程序的运行结果是否和预期的相同了。

164 | 165 |

我们来看看这个math包的测试用例。

166 | 167 |
package math
168 | 
169 | import (
170 |     "testing"
171 | )
172 | 
173 | func TestAdd(t *testing.T) {
174 |     var a = 100
175 |     var b = 200
176 | 
177 |     var val = Add(a, b)
178 |     if val != a+b {
179 |         t.Error("Test Case [", "TestAdd", "] Failed!")
180 |     }
181 | }
182 | 
183 | func TestSubtract(t *testing.T) {
184 |     var a = 100
185 |     var b = 200
186 | 
187 |     var val = Subtract(a, b)
188 |     if val != a-b {
189 |         t.Error("Test Case [", "TestSubtract", "] Failed!")
190 |     }
191 | }
192 | 
193 | func TestMultiply(t *testing.T) {
194 |     var a = 100
195 |     var b = 200
196 | 
197 |     var val = Multiply(a, b)
198 |     if val != a*b {
199 |         t.Error("Test Case [", "TestMultiply", "] Failed!")
200 |     }
201 | }
202 | 
203 | func TestDivideNormal(t *testing.T) {
204 |     var a = 100
205 |     var b = 200
206 | 
207 |     var val = Divide(a, b)
208 |     if val != a/b {
209 |         t.Error("Test Case [", "TestDivideNormal", "] Failed!")
210 |     }
211 | }
212 | 
213 | 214 |

将路径切换到测试文件所在目录,运行go test命令,go会自动测试所有的测试用例。

215 | 216 |

在上面的例子中,测试用例的特点是以函数名以Test开始,而且具有唯一参数t *testing.T

217 | 218 |

小结

219 | 220 |

Go提供的包和用例测试是构建优秀的软件产品的基础,只要我们不断学习,努力去做,一定可以做的很好。

221 | 222 | -------------------------------------------------------------------------------- /GolangStudy/tutorial/go_tutorial/go_tutorial_1_how_to_install_go.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Go语言环境安装与测试 10 | 11 | 12 | 13 |

安装

14 | 15 |

首先来谈谈Go语言的安装,要使用Go来编写程序首先得把环境搭建起来。 16 | Go的语言环境搭建还是比较简单的。Google提供了Windows和Mac的安装包,所以去下载一下安装就可以了。 17 | 对于Linux的系统,可以使用系统提供的包安装工具来安装。

18 | 19 |

Go的下载地址

20 | 21 |

https://code.google.com/p/go/downloads/list

22 | 23 |

Windows

24 | 25 |

对于Windows系统,Go提供了两种不同的安装包,分别对应32位的系统和64位的系统,安装的时候根据自己的系统实际情况选择下载包。Windows下面提供的是msi格式的安装包,这种包是可执行文件,直接双击安装就可以了。安装完成之后,安装程序会自动地将安装完的Go的根目录下的bin目录加入系统的PATH环境变量里面。所以直接打开命令行,输入go,就可以看到一些提示信息了。

26 | 27 |

Mac

28 | 29 |

如果是新买的Mac,里面可能自带了一个go的可执行文件,在路径/etc/paths.d/下面,就是一个go可执行文件。如果我们需要安装从官网下载的dmg安装包,先要把这个文件删除掉。可以用sudo rm /etc/paths.d/go来删除。然后自动安装dmg之后,要使用export PATH的方法将安装好的Go目录下面的bin目录加入PATH中。一般安装完之后路径为/usr/local/go,所以你可以用下面的方法: 30 | 首先切换到自己的用户目录

31 | 32 |
cd ~
 33 | 
34 | 35 |

然后

36 | 37 |
vim .profile
 38 | 
39 | 40 |

加入一行

41 | 42 |
export PATH=/usr/local/go/bin:$PATH
 43 | 
44 | 45 |

就可以了。

46 | 47 |

Linux

48 | 49 |

Linux的发行版有很多,可以根据不同系统提供的包管理工具来安装Go,不过可能系统包管理工具提供的不是最新的Go版本。在这种情况下,你可以去下载最新的tar包。 50 | 然后使用下面的方法

51 | 52 |
sudo tar -C /usr/local -xzf go1.2.linux-386.tar.gz
 53 | 
54 | 55 |

如果是64位的系统,用下面的方法

56 | 57 |
sudo tar -C /usr/local -xzf go1.2.linux-amd64.tar.gz
 58 | 
59 | 60 |

当然,这样的方式只是将安装包解压拷贝到/usr/local/下面。你还需要使用export PATH的方式将Go的bin目录加入PATH。 61 | 方法和上面Mac介绍的一样。 62 | 另外如果你不是将Go安装到/usr/local目录下面,你还需要设置一个GOROOT环境变量。比如你安装到你自己的文件夹下面,比如叫jemy的用户的路径是/home/jemy,那么你安装到这个目录的Go路径为/home/jemy/go,那么在export PATH之前,你还需要使用下面的命令。

63 | 64 |
export GOROOT=/home/jemy/go
 65 | 
66 | 67 |

总结一下,如果你默认安装路径为/usr/local/go,那么只需要用

68 | 69 |
export PATH=$PATH:/usr/local/go/bin
 70 | 
71 | 72 |

就可以了。 73 | 如果不是默认路径则需要这样

74 | 75 |
export GOROOT=/home/jemy/go
 76 | export PATH=$PATH:/$GROOT/bin
 77 | 
78 | 79 |

上面的/home/jemy是根据实际安装的路径情况来确定。

80 | 81 |

最后说一下go的最基本的三个命令

82 | 83 |

1.查看版本号

84 | 85 |
go version 
 86 | 
87 | 88 |

结果为

89 | 90 |
duokr:~ jemy$ go version
 91 | go version go1.2 darwin/386
 92 | 
93 | 94 |

2.格式化go代码文件

95 | 96 |
go fmt file_name.go
 97 | 
98 | 99 |

3.运行单个go代码文件

100 | 101 |
go run file_name.go
102 | 
103 | 104 |

测试

105 | 106 |

hello world

107 | 108 |

学习计算机的, 绕不开的三件事。

109 | 110 |

有谁安装好语言环境,不试一下hello world的?

111 | 112 |
//main包, 凡是标注为main包的go文件都会被编译为可执行文件
113 | package main
114 | 
115 | //导入需要使用的包
116 | import (
117 |     "fmt" //支持格式化输出的包,就是format的简写
118 | )
119 | 
120 | //主函数,程序执行入口
121 | func main() {
122 |     /*
123 |         输出一行hello world
124 |         Println函数就是print line的意思
125 |     */
126 |     fmt.Println("hello world")
127 | }
128 | 
129 | 130 |

然后使用go run helloworld.go来运行这个例子。如果安装成功,那么会输出一行hello world

131 | 132 |

PS

133 | 134 |

Windows7可以在文件所在目录下面使用Shift+右键,快速打开已定位到所在目录的命令行窗口。直接输入上面命令即可。

135 | 136 | -------------------------------------------------------------------------------- /GolangStudy/tutorial/go_tutorial/go_tutorial_2_data_type.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Go语言内置基础数据类型 10 | 11 | 12 | 13 |

在自然界里面,有猫,有狗,有猪。有各种动物。每种动物都是不同的。
14 | 比如猫会喵喵叫,狗会旺旺叫,猪会哼哼叫。。。
15 | Stop!!!
16 | 好了,大家毕竟不是幼儿园的小朋友。介绍到这里就可以了。

17 | 18 |

论点就是每个东西都有自己归属的类别(Type)。
19 | 那么在Go语言里面,每个变量也都是有类别的,这种类别叫做数据类型(Data Type)
20 | Go的数据类型有两种:一种是语言内置的数据类型,另外一种是通过语言提供的自定义数据类型方法自己定义的自定义数据类型

21 | 22 |

先看看语言内置的基础数据类型

23 | 24 |

数值型(Number)

25 | 26 |

数值型有三种,一种是整数类型,另外一种是带小数的类型(一般计算机里面叫做浮点数类型),还有一种虚数类型

27 | 28 |

整数类型不用说了,和数学里面的是一样的。和数学里面不同的地方在于计算机里面正整数和零统称为无符号整型,而负整数则称为有符号整型

29 | 30 |

Go的内置整型有uint8, uint16, uint32, uint64, int8, int16, int32int64。其中u开头的类型就是无符号整型。无符号类型能够表示正整数和零。而有符号类型除了能够表示正整数和零外,还可以表示负整数。 31 | 另外还有一些别名类型,比如byte类型,这个类型和uint8是一样的,表示字节类型。另外一个是rune类型,这个类型和int32是一样的,用来表示unicode的代码点,就是unicode字符所对应的整数。

32 | 33 |

Go还定义了三个依赖系统的类型,uintintuintptr。因为在32位系统和64位系统上用来表示这些类型的位数是不一样的。

34 | 35 |

对于32位系统

36 | 37 |

uint=uint32
38 | int=int32
39 | uintptr为32位的指针

40 | 41 |

对于64位系统

42 | 43 |

uint=uint64
44 | int=int64
45 | uintptr为64位的指针

46 | 47 |

至于类型后面跟的数字8,16,32或是64则表示用来表示这个类型的位不同,位越多,能表示的整数范围越大。 48 | 比如对于用N位来表示的整数,如果是有符号的整数,能够表示的整数范围为-2^(N-1) ~ 2^(N-1)-1;如果是无符号的整数,则能表示的整数范围为0 ~ 2^N

49 | 50 |

Go的浮点数类型有两种,float32float64。float32又叫单精度浮点型,float64又叫做双精度浮点型。其最主要的区别就是小数点后面能跟的小数位数不同

51 | 52 |

另外Go还有两个其他语言所没有的类型,虚数类型complex64complex128

53 | 54 |

对于数值类型,其所共有的操作为加法(+)减法(-)乘法(*)除法(/)。另外对于整数类型,还定义了求余运算(%)

55 | 56 |

求余运算为整型所独有。如果对浮点数使用求余,比如这样

57 | 58 |
package main
 59 | 
 60 | import (
 61 |     "fmt"
 62 | )
 63 | 
 64 | func main() {
 65 |     var a float64 = 12
 66 |     var b float64 = 3
 67 | 
 68 |     fmt.Println(a % b)
 69 | }
 70 | 
71 | 72 |

编译时候会报错

73 | 74 |
invalid operation: a % b (operator % not defined on float64)
 75 | 
76 | 77 |

所以,这里我们可以知道所谓的数据类型有两层意思,一个是定义了该类型所能表示的数,另一个是定义了该类型所能进行的操作。 78 | 简单地说,对于一只小狗,你能想到的一定是狗的面貌和它会汪汪叫,而不是猫的面容和喵喵叫。

79 | 80 |

字符串类型(String)

81 | 82 |

字符串就是一串固定长度的字符连接起来的字符序列。Go的字符串是由单个字节连接起来的。(对于汉字,通常由多个字节组成)。这就是说,传统的字符串是由字符组成的,而Go的字符串不同,是由字节组成的。这一点需要注意。

83 | 84 |

字符串的表示很简单。用(双引号"")或者(``号)来描述。

85 | 86 |
"hello world"
 87 | 
88 | 89 |

或者

90 | 91 |
`hello world`
 92 | 
93 | 94 |

唯一的区别是,双引号之间的转义字符会被转义,而``号之间的转义字符保持原样不变

95 | 96 |
package main
 97 | 
 98 | import (
 99 |     "fmt"
100 | )
101 | 
102 | func main() {
103 |     var a = "hello \n world"
104 |     var b = `hello \n world`
105 | 
106 |     fmt.Println(a)
107 |     fmt.Println("----------")
108 |     fmt.Println(b)
109 | }
110 | 
111 | 112 |

输出结果为

113 | 114 |
hello 
115 |  world
116 | ----------
117 | hello \n world
118 | 
119 | 120 |

字符串所能进行的一些基本操作包括:
121 | (1)获取字符长度
122 | (2)获取字符串中单个字节
123 | (3)字符串连接

124 | 125 |
package main
126 | 
127 | import (
128 |     "fmt"
129 | )
130 | 
131 | func main() {
132 |     var a string = "hello"
133 |     var b string = "world"
134 | 
135 |     fmt.Println(len(a))
136 |     fmt.Println(a[1])
137 |     fmt.Println(a + b)
138 | }
139 | 
140 | 141 |

输出如下

142 | 143 |
5
144 | 101
145 | helloworld
146 | 
147 | 148 |

这里我们看到a[1]得到的是一个整数,这就证明了上面"Go的字符串是由字节组成的这句话"。我们还可以再验证一下。

149 | 150 |
package main
151 | 
152 | import (
153 |     "fmt"
154 | )
155 | 
156 | func main() {
157 |     var a string = "你"
158 |     var b string = "好"
159 |     fmt.Println(len(a))
160 |     fmt.Println(len(b))
161 |     fmt.Println(len(a + b))
162 |     fmt.Println(a[0])
163 |     fmt.Println(a[1])
164 |     fmt.Println(a[2])
165 | }
166 | 
167 | 168 |

输出

169 | 170 |
3
171 | 3
172 | 6
173 | 228
174 | 189
175 | 160
176 | 
177 | 178 |

我们开始的时候,从上面的三行输出知道,"你"和"好"分别是用三个字节组成的。我们依次获取a的三个字节,输出,得到结果。

179 | 180 |

布尔型(Bool)

181 | 182 |

布尔型是表示真值假值的类型。可选值为truefalse

183 | 184 |

所能进行的操作如下: 185 | && and 与 186 | || or 或 187 | ! not 非

188 | 189 |

Go的布尔型取值就是truefalse任何空值(nil)或者零值(0, 0.0, "")都不能作为布尔型来直接判断

190 | 191 |
package main
192 | 
193 | import (
194 |     "fmt"
195 | )
196 | 
197 | func main() {
198 |     var equal bool
199 |     var a int = 10
200 |     var b int = 20
201 |     equal = (a == b)
202 |     fmt.Println(equal)
203 | }
204 | 
205 | 206 |

输出结果

207 | 208 |
false
209 | 
210 | 211 |

下面是错误的用法

212 | 213 |
package main
214 | 
215 | import (
216 |     "fmt"
217 | )
218 | 
219 | func main() {
220 |     if 0 {
221 |         fmt.Println("hello world")
222 |     }
223 |     if nil {
224 |         fmt.Println("hello world")
225 |     }
226 |     if "" {
227 |         fmt.Println("hello world")
228 |     }
229 | }
230 | 
231 | 232 |

编译错误

233 | 234 |
./t.go:8: non-bool 0 (type untyped number) used as if condition
235 | ./t.go:11: non-bool nil used as if condition
236 | ./t.go:14: non-bool "" (type untyped string) used as if condition
237 | 
238 | 239 |

上面介绍的是Go语言内置的基础数据类型。

240 | 241 | -------------------------------------------------------------------------------- /GolangStudy/tutorial/go_tutorial/go_tutorial_3_variable.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 变量和常量定义 10 | 11 | 12 | 13 |

现在我们讨论一下Go语言的变量定义。

14 | 15 |

变量定义

16 | 17 |

所谓的变量就是一个拥有指定名称类型数据存储位置
18 | 在上面我们使用过变量的定义,现在我们来仔细看一个例子。

19 | 20 |
package main
 21 | 
 22 | import (
 23 |     "fmt"
 24 | )
 25 | 
 26 | func main() {
 27 |     var x string = "hello world"
 28 |     fmt.Println(x)
 29 | }
 30 | 
31 | 32 |

变量的定义首先使用var关键字,然后指定变量的名称x,再指定变量的类型string,在本例中,还对变量x进行了赋值,然后在命令行输出该变量。Go这种变量定义的方式和其他的语言有些不同,但是在使用的过程中,你会逐渐喜欢的。当然上面的变量定义方式还可以如下,即先定义变量,再赋值。

33 | 34 |
package main
 35 | 
 36 | import (
 37 |     "fmt"
 38 | )
 39 | 
 40 | func main() {
 41 |     var x string
 42 |     x = "hello world"
 43 |     fmt.Println(x)
 44 | }
 45 | 
46 | 47 |

或者是直接赋值,让Go语言推断变量的类型。如下:

48 | 49 |
package main
 50 | 
 51 | import (
 52 |     "fmt"
 53 | )
 54 | 
 55 | func main() {
 56 |     var x = "hello world"
 57 |     fmt.Println(x)
 58 | }
 59 | 
60 | 61 |

当然,上面变量的定义还有一种快捷方式。如果你知道变量的初始值,完全可以像下面这样定义变量,完全让Go来推断语言的类型。这种定义的方式连关键字var都省略掉了。

62 | 63 |
package main
 64 | 
 65 | import (
 66 |     "fmt"
 67 | )
 68 | 
 69 | func main() {
 70 |     x := "hello world"
 71 |     fmt.Println(x)
 72 | }
 73 | 
74 | 75 |

注意:上面这种使用:=方式定义变量的方式只能用在函数内部

76 | 77 |
package main
 78 | 
 79 | import (
 80 |     "fmt"
 81 | )
 82 | 
 83 | x:="hello world"
 84 | func main() {
 85 |     y := 10
 86 |     fmt.Println(x)
 87 |     fmt.Println(y)
 88 | }
 89 | 
90 | 91 |

对于上面的变量定义x是无效的。会导致编译错误:

92 | 93 |
./test_var_quick.go:7: non-declaration statement outside function body
 94 | 
95 | 96 |

不过我们对上面的例子做下修改,比如这样是可以的。也就是使用var关键字定义的时候,如果给出初始值,就不需要显式指定变量类型。

97 | 98 |
package main
 99 | 
100 | import (
101 |     "fmt"
102 | )
103 | 
104 | var x = "hello world"
105 | 
106 | func main() {
107 |     y := 10
108 |     fmt.Println(x)
109 |     fmt.Println(y)
110 | }
111 | 
112 | 113 |

变量之所以称为变量,就是因为它们的值在程序运行过程中可以发生变化,但是它们的变量类型是无法改变的。因为Go语言是静态语言,并不支持程序运行过程中变量类型发生变化。比如如果你强行将一个字符串值赋值给定义为int的变量,那么会发生编译错误。即使是强制类型转换也是不可以的。`强制类型转换只支持同类的变量类型``。比如数值类型之间强制转换。

114 | 115 |

下面我们看几个例子:

116 | 117 |
package main
118 | 
119 | import (
120 |     "fmt"
121 | )
122 | 
123 | func main() {
124 |     var x string = "hello world"
125 |     fmt.Println(x)
126 |     x = "i love go language"
127 |     fmt.Println(x)
128 | }
129 | 
130 | 131 |

本例子演示变量的值在程序运行过程中发生变化,结果输出为

132 | 133 |
hello world
134 | i love go language
135 | 
136 | 137 |

我们尝试不同类型的变量之间转换

138 | 139 |
package main
140 | 
141 | import (
142 |     "fmt"
143 | )
144 | 
145 | func main() {
146 |     var x string = "hello world"
147 |     fmt.Println(x)
148 |     x = 11
149 |     fmt.Println(x)
150 | }
151 | 
152 | 153 |

在本例子中,如果试图将一个数值赋予字符串变量x,那么会发生错误:

154 | 155 |
./test_var.go:10: cannot use 11 (type int) as type string in assignment
156 | 
157 | 158 |

上面的意思就是无法将整型数值11当作字符串赋予给字符串变量。

159 | 160 |

但是同类的变量之间是可以强制转换的,如浮点型和整型之间的转换。

161 | 162 |
package main
163 | 
164 | import (
165 |     "fmt"
166 | )
167 | 
168 | func main() {
169 |     var x float64 = 32.35
170 |     fmt.Println(x)
171 |     fmt.Println(int(x))
172 | }
173 | 
174 | 175 |

输出的结果为

176 | 177 |
32.35
178 | 32
179 | 
180 | 181 |

变量命名

182 | 183 |

上面我们看了一些变量的使用方法,那么定义一个变量名称,有哪些要求呢? 184 | 这里我们要注意,Go的变量名称必须以字母或下划线(_)开头,后面可以跟字母,数字,或者下划线(_)。除此之外,Go语言并不关心你如何定义变量。我们通用的做法是定义一个用户友好的变量。假设你需要定义一个狗狗的年龄,那么使用dog_age作为变量名称要好于用x来定义变量。

185 | 186 |

变量作用域

187 | 188 |

现在我们再来讨论一下变量的作用域。所谓作用域就是可以有效访问变量的区域。比如很简单的,你不可能在一个函数func_a里面访问另一个函数func_b里面定义的局部变量x。所以变量的作用域目前分为两类,一个是全局变量,另一个是局部变量。下面我们看个全局变量的例子:

189 | 190 |
package main
191 | 
192 | import (
193 |     "fmt"
194 | )
195 | 
196 | var x string = "hello world"
197 | 
198 | func main() {
199 |     fmt.Println(x)
200 | }
201 | 
202 | 203 |

这里变量x定义在main函数之外,但是main函数仍然可以访问x。全局变量的作用域是该包中所有的函数。

204 | 205 |
package main
206 | 
207 | import (
208 |     "fmt"
209 | )
210 | 
211 | var x string = "hello world"
212 | 
213 | func change() {
214 |     x = "i love go"
215 | }
216 | func main() {
217 |     fmt.Println(x)
218 |     change()
219 |     fmt.Println(x)
220 | }
221 | 
222 | 223 |

在上面的例子用,我们用了change函数改变了x的值。输出结果如下:

224 | 225 |
hello world
226 | i love go
227 | 
228 | 229 |

我们再看一下局部变量的例子。

230 | 231 |
package main
232 | 
233 | import (
234 |     "fmt"
235 | )
236 | 
237 | func change() {
238 |     x := "i love go"
239 | }
240 | func main() {
241 |     fmt.Println(x)
242 | }
243 | 
244 | 245 |

该例子中main函数试图访问change函数中定义的局部变量x,结果发生了下面的错误(未定义的变量x):

246 | 247 |
./test_var.go:11: undefined: x
248 | 
249 | 250 |

常量

251 | 252 |

Go语言也支持常量定义。所谓常量就是在程序运行过程中保持值不变的变量定义。常量的定义和变量类似,只是用const关键字替换了var关键字,另外常量在定义的时候必须有初始值

253 | 254 |
package main
255 | 
256 | import (
257 |     "fmt"
258 | )
259 | 
260 | func main() {
261 |     const x string = "hello world"
262 |     const y = "hello world"
263 |     fmt.Println(x)
264 |     fmt.Println(y)
265 | }
266 | 
267 | 268 |

这里有一点需要注意,变量定义的类型推断方式:=不能够用来定义常量。因为常量的值是在编译的时候就已经确定的,但是变量的值则是运行的时候才使用的。这样常量定义就无法使用变量类型推断的方式了。

269 | 270 |

常量的值在运行过程中是无法改变的,强制改变常量的值是无效的。

271 | 272 |
package main
273 | 
274 | import (
275 |     "fmt"
276 | )
277 | 
278 | func main() {
279 |     const x string = "hello world"
280 |     fmt.Println(x)
281 |     x = "i love go language"
282 |     fmt.Println(x)
283 | }
284 | 
285 | 286 |

比如上面的例子就会报错

287 | 288 |
./test_var.go:10: cannot assign to x
289 | 
290 | 291 |

我们再看一个Go包math里面定义的常量Pi,用它来求圆的面积。

292 | 293 |
package main
294 | 
295 | import (
296 |     "fmt"
297 |     "math"
298 | )
299 | 
300 | func main() {
301 |     var radius float64 = 10
302 |     var area = math.Pow(radius, 2) * math.Pi
303 |     fmt.Println(area)
304 | }
305 | 
306 | 307 |

多变量或常量定义

308 | 309 |

Go还提供了一种同时定义多个变量或者常量的快捷方式。

310 | 311 |
package main
312 | 
313 | import (
314 |     "fmt"
315 | )
316 | 
317 | func main() {
318 |     var (
319 |         a int     = 10
320 |         b float64 = 32.45
321 |         c bool    = true
322 |     )
323 |     const (
324 |         Pi   float64 = 3.14
325 |         True bool    = true
326 |     )
327 | 
328 |     fmt.Println(a, b, c)
329 |     fmt.Println(Pi, True)
330 | }
331 | 
332 | 333 | -------------------------------------------------------------------------------- /GolangStudy/tutorial/go_tutorial/go_tutorial_4_control_structure.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 程序控制结构 10 | 11 | 12 | 13 |

虽然剧透可耻,但是为了体现Go语言的设计简洁之处,必须要先剧透一下。

14 | 15 |

Go语言的控制结构关键字只有

16 | 17 |

if..else if..elseforswitch

18 | 19 |

而且在Go中,为了避免格式化战争,对程序结构做了统一的强制的规定。看下下面的例子。

20 | 21 |

请比较一下A程序和B程序的不同之处。

22 | 23 |

A程序

24 | 25 |
package main
 26 | 
 27 | import (
 28 |     "fmt"
 29 | )
 30 | 
 31 | func main() {
 32 |     fmt.Println("hello world")
 33 | }
 34 | 
35 | 36 |

B程序

37 | 38 |
package main
 39 | 
 40 | import (
 41 |     "fmt"
 42 | )
 43 | 
 44 | func main() 
 45 | {
 46 |     fmt.Println("hello world")
 47 | }
 48 | 
49 | 50 |

还记得我们前面的例子中,{}的格式是怎么样的么?在上面的两个例子中只有A例的写法是对的。因为在Go语言中,强制了{}的格式。如果我们试图去编译B程序,那么会发生如下的错误提示。

51 | 52 |
./test_format.go:9: syntax error: unexpected semicolon or newline before {
 53 | 
54 | 55 |

if..else if..else

56 | 57 |

if..else if..else 用来判断一个或者多个条件,然后根据条件的结果执行不同的程序块。举个简单的例子。

58 | 59 |
package main
 60 | 
 61 | import (
 62 |     "fmt"
 63 | )
 64 | 
 65 | func main() {
 66 |     var dog_age = 10
 67 | 
 68 |     if dog_age > 10 {
 69 |         fmt.Println("A big dog")
 70 |     } else if dog_age > 1 && dog_age <= 10 {
 71 |         fmt.Println("A small dog")
 72 |     } else {
 73 |         fmt.Println("A baby dog")
 74 |     }
 75 | }
 76 | 
77 | 78 |

上面的例子判断狗狗的年龄如果(if)大于10就是一个大狗;否则判断(else if)狗狗的年龄是否小于等于10且大于1,这个时候狗狗是小狗狗。否则(else)的话(就是默认狗狗的年龄小于等于1岁),那么狗狗是Baby狗狗。

79 | 80 |

在上面的例子中,我们还可以发现Go的if..else if..else语句的判断条件一般都不需要使用()。当然如果你还是愿意写,也是对的。另外如果为了将某两个或多个条件绑定在一起判断的话,还是需要括号()的。

81 | 82 |

比如下面的例子也是对的。

83 | 84 |
package main
 85 | 
 86 | import (
 87 |     "fmt"
 88 | )
 89 | 
 90 | func main() {
 91 |     const Male = 'M'
 92 |     const Female = 'F'
 93 | 
 94 |     var dog_age = 10
 95 |     var dog_sex = 'M'
 96 | 
 97 |     if (dog_age == 10 && dog_sex == 'M') {
 98 |         fmt.Println("dog")
 99 |     }
100 | }
101 | 
102 | 103 |

但是如果你使用Go提供的格式化工具来格式化这段代码的话,Go会智能判断你的括号是否必须有,否则的话,会帮你去掉的。你可以试试。

104 | 105 |
go fmt test_bracket.go
106 | 
107 | 108 |

然后你会发现,咦?!果真被去掉了。

109 | 110 |

另外因为每个判断条件的结果要么是true要么是false,所以可以使用&&||来连接不同的条件。使用!来对一个条件取反。

111 | 112 |

switch

113 | 114 |

switch的出现是为了解决某些情况下使用if判断语句带来的繁琐之处。

115 | 116 |

例如下面的例子:

117 | 118 |
package main
119 | 
120 | import (
121 |     "fmt"
122 | )
123 | 
124 | func main() {
125 |     //score 为 [0,100]之间的整数
126 |     var score int = 69
127 | 
128 |     if score >= 90 && score <= 100 {
129 |         fmt.Println("优秀")
130 |     } else if score >= 80 && score < 90 {
131 |         fmt.Println("良好")
132 |     } else if score >= 70 && score < 80 {
133 |         fmt.Println("一般")
134 |     } else if score >= 60 && score < 70 {
135 |         fmt.Println("及格")
136 |     } else {
137 |         fmt.Println("不及格")
138 |     }
139 | }
140 | 
141 | 142 |

在上面的例子中,我们用if..else if..else来对分数进行分类。这个只是一般的情况下if判断条件的数量。如果if..else if..else的条件太多的话,我们可以使用switch来优化程序。比如上面的程序我们还可以这样写:

143 | 144 |
package main
145 | 
146 | import (
147 |     "fmt"
148 | )
149 | 
150 | func main() {
151 |     //score 为 [0,100]之间的整数
152 |     var score int = 69
153 | 
154 |     switch score / 10 {
155 |     case 10:
156 |     case 9:
157 |         fmt.Println("优秀")
158 |     case 8:
159 |         fmt.Println("良好")
160 |     case 7:
161 |         fmt.Println("一般")
162 |     case 6:
163 |         fmt.Println("及格")
164 |     default:
165 |         fmt.Println("不及格")
166 |     }
167 | }
168 | 
169 | 170 |

关于switch的几点说明如下:

171 | 172 |

(1) switch的判断条件可以为任何数据类型。

173 | 174 |
package main
175 | 
176 | import (
177 |     "fmt"
178 | )
179 | 
180 | func main() {
181 |     var dog_sex = "F"
182 |     switch dog_sex {
183 |     case "M":
184 |         fmt.Println("A male dog")
185 |     case "F":
186 |         fmt.Println("A female dog")
187 |     }
188 | }
189 | 
190 | 191 |

(2) 每个case后面跟的是一个完整的程序块,该程序块不需要{},也不需要break结尾,因为每个case都是独立的。

192 | 193 |

(3) 可以为switch提供一个默认选项default,在上面所有的case都没有满足的情况下,默认执行default后面的语句。

194 | 195 |

for

196 | 197 |

for用在Go语言的循环条件里面。比如说要你输出1...100之间的自然数。最笨的方法就是直接这样。

198 | 199 |
package main
200 | 
201 | import (
202 |     "fmt"
203 | )
204 | 
205 | func main() {
206 |     fmt.Println(1)
207 |     fmt.Println(2)
208 |     ...
209 |     fmt.Println(100)
210 | }
211 | 
212 | 213 |

这个不由地让我想起一个笑话。

214 | 215 |

以前一个地主的儿子学习写字,只学了三天就把老师赶走了。因为在这三天里面他学写了一,二,三。他觉得写字真的太简单了,不就是画横线嘛。于是有一天老爹过寿,让他来记送礼的人名单。直到中午还没有记完,老爹很奇怪就去问他怎么了。他哭着说,“不知道这个人有什么毛病,姓什么不好,姓万”。

216 | 217 |

哈哈,回来继续。我们看到上面的例子也是如地主的儿子那样就不好了。所以,我们必须使用循环结构。我们用for的循环语句来实现上面的例子。

218 | 219 |
package main
220 | 
221 | import (
222 |     "fmt"
223 | )
224 | 
225 | func main() {
226 |     var i int = 1
227 | 
228 |     for ; i <= 100; i++ {
229 |         fmt.Println(i)
230 |     }
231 | }
232 | 
233 | 234 |

在上面的例子中,首先初始化变量i为1,然后在for循环里面判断是否小于等于100,如果是的话,输出i,然后再使用i++来将i的值自增1。上面的例子,还有一个更好的写法,就是将i的定义和初始化也放在for里面。如下:

235 | 236 |
package main
237 | 
238 | import (
239 |     "fmt"
240 | )
241 | 
242 | func main() {
243 |     for i := 1; i <= 100; i++ {
244 |         fmt.Println(i)
245 |     }
246 | }
247 | 
248 | 249 |

在Go里面没有提供while关键字,如果你怀念while的写法也可以这样:

250 | 251 |
package main
252 | 
253 | import (
254 |     "fmt"
255 | )
256 | 
257 | func main() {
258 |     var i int = 1
259 | 
260 |     for i <= 100 {
261 |         fmt.Println(i)
262 |         i++
263 |     }
264 | }
265 | 
266 | 267 |

或许你会问,如果我要死循环呢?是不是for true?呵呵,不用了,直接这样。

268 | 269 |
for{
270 |     ...
271 | }
272 | 
273 | 274 |

以上就是Go提供的全部控制流程了。

275 | 276 |

再复习一下,Go只提供了:

277 | 278 |

if

279 | 280 |
if ...{
281 |     ...
282 | }else if ...{
283 |     ...
284 | }else{
285 |     ...
286 | }
287 | 
288 | 289 |

switch

290 | 291 |
switch(...){
292 | case ...:
293 |          ...
294 | case ...:
295 |          ...
296 | ...
297 | 
298 | default:
299 |           ...
300 | }
301 | 
302 | 303 |

for

304 | 305 |
for ...; ...; ...{
306 |     ...
307 | }
308 | 
309 | for ...{
310 |     ...
311 | }
312 | 
313 | for{
314 |     ...
315 | }
316 | 
317 | 318 | -------------------------------------------------------------------------------- /GolangStudy/tutorial/go_tutorial/go_tutorial_7_pointer.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Go指针 10 | 11 | 12 | 13 |

不要害怕,Go的指针是好指针。

14 | 15 |

定义

16 | 17 |

所谓指针其实你可以把它想像成一个箭头,这个箭头指向(存储)一个变量的地址

18 | 19 |

因为这个箭头本身也需要变量来存储,所以也叫做指针变量。

20 | 21 |

Go的指针不支持那些乱七八糟的指针移位它就表示一个变量的地址。看看这个例子:

22 | 23 |
package main
 24 | 
 25 | import (
 26 |     "fmt"
 27 | )
 28 | 
 29 | func main() {
 30 |     var x int
 31 |     var x_ptr *int
 32 | 
 33 |     x = 10
 34 |     x_ptr = &x
 35 | 
 36 |     fmt.Println(x)
 37 |     fmt.Println(x_ptr)
 38 |     fmt.Println(*x_ptr)
 39 | }
 40 | 
41 | 42 |

上面例子输出x的值x的地址通过指针变量输出x的值,而x_ptr就是一个指针变量

43 | 44 |
10
 45 | 0xc084000038
 46 | 10
 47 | 
48 | 49 |

认真理清楚这两个符号的意思。

50 | 51 |

& 取一个变量的地址

52 | 53 |

* 取一个指针变量所指向的地址的值

54 | 55 |

考你一下,上面的例子中,如何输出x_ptr的地址呢?

56 | 57 |
package main
 58 | 
 59 | import (
 60 |     "fmt"
 61 | )
 62 | 
 63 | func main() {
 64 |     var x int
 65 |     var x_ptr *int
 66 | 
 67 |     x = 10
 68 |     x_ptr = &x
 69 | 
 70 |     fmt.Println(&x_ptr)
 71 | }
 72 | 
73 | 74 |

此例看懂,指针就懂了。

75 | 76 |

永远记住一句话,所谓指针就是一个指向(存储)特定变量地址的变量。没有其他的特别之处。

77 | 78 |

再变态一下,看看这个:

79 | 80 |
package main
 81 | 
 82 | import (
 83 |     "fmt"
 84 | )
 85 | 
 86 | func main() {
 87 |     var x int
 88 |     var x_ptr *int
 89 | 
 90 |     x = 10
 91 |     x_ptr = &x
 92 | 
 93 |     fmt.Println(*&x_ptr)
 94 | }
 95 | 
96 | 97 |
    98 |
  1. x_ptr 是一个指针变量,它指向(存储)x的地址
  2. 99 |
  3. &x_ptr 是取这个指针变量x_ptr的地址,这里可以设想有另一个指针变量x_ptr_ptr(指向)存储这个x_ptr指针的地址
  4. 100 |
  5. *&x_ptr 等价于*x_ptr_ptr就是取这个x_ptr_ptr指针变量指向(存储)地址所对应的变量的值 ,也就是x_ptr的值,也就是指针变量x_ptr指向(存储)的地址,也就是x的地址。 这里可以看到,其实*&这两个运算符在一起就相互抵消作用了。
  6. 101 |
102 | 103 | 104 |

用途

105 | 106 |

指针的一大用途就是可以将变量的指针作为实参传递给函数,从而在函数内部能够直接修改实参所指向的变量值。

107 | 108 |

Go的变量传递都是值传递。

109 | 110 |
package main
111 | 
112 | import (
113 |     "fmt"
114 | )
115 | 
116 | func change(x int) {
117 |     x = 200
118 | }
119 | func main() {
120 |     var x int = 100
121 |     fmt.Println(x)
122 |     change(x)
123 |     fmt.Println(x)
124 | }
125 | 
126 | 127 |

上面的例子输出结果为

128 | 129 |
100
130 | 100
131 | 
132 | 133 |

很显然,change函数改变的仅仅是内部变量x,而不会改变传递进去的实参。其实,也就是说Go的函数一般关心的是输出结果,而输入参数就相当于信使跑到函数门口大叫,你们这个参数是什么值,那个是什么值,然后就跑了。你函数根本就不能修改它的值。不过如果是传递的实参是指针变量,那么函数一看,小子这次你地址我都知道了,哪里跑。那么就是下面的例子:

134 | 135 |
package main
136 | 
137 | import (
138 |     "fmt"
139 | )
140 | 
141 | func change(x *int) {
142 |     *x = 200
143 | }
144 | func main() {
145 |     var x int = 100
146 |     fmt.Println(x)
147 |     change(&x)
148 |     fmt.Println(x)
149 | }
150 | 
151 | 152 |

上面的例子中,change函数的虚参为整型指针变量,所以在main中调用的时候传递的是x的地址。然后在change里面使用*x=200修改了这个x的地址的值。所以x的值就变了。这个输出是:

153 | 154 |
100
155 | 200
156 | 
157 | 158 |

new

159 | 160 |

new这个函数挺神奇,因为它的用处太多了。这里还可以通过new来初始化一个指针。上面说过指针指向(存储)的是一个变量的地址,但是指针本身也需要地址存储。先看个例子:

161 | 162 |
package main
163 | 
164 | import (
165 |     "fmt"
166 | )
167 | 
168 | func set_value(x_ptr *int) {
169 |     *x_ptr = 100
170 | }
171 | func main() {
172 |     x_ptr := new(int)
173 |     set_value(x_ptr)
174 |     //x_ptr指向的地址
175 |     fmt.Println(x_ptr)
176 |     //x_ptr本身的地址
177 |     fmt.Println(&x_ptr)
178 |     //x_ptr指向的地址值
179 |     fmt.Println(*x_ptr)
180 | }
181 | 
182 | 183 |

上面我们定义了一个x_ptr变量,然后用new申请了一个存储整型数据的内存地址,然后将这个地址赋值x_ptr指针变量,也就是说x_ptr指向(存储)的是一个可以存储整型数据的地址,然后用set_value函数将这个地址中存储的值赋值为100。所以第一个输出是x_ptr指向的地址,第二个则是x_ptr本身的地址,而*x_ptr则是x_ptr指向的地址中存储的整型数据的值

184 | 185 |
0xc084000040
186 | 0xc084000038
187 | 100
188 | 
189 | 190 |

小结

191 | 192 |

好了,现在用个例子再来回顾一下指针。

193 | 194 |

交换两个变量的值。

195 | 196 |
package main
197 | 
198 | import (
199 |     "fmt"
200 | )
201 | 
202 | func swap(x, y *int) {
203 |     *x, *y = *y, *x
204 | }
205 | func main() {
206 |     x_val := 100
207 |     y_val := 200
208 |     swap(&x_val, &y_val)
209 |     fmt.Println(x_val)
210 |     fmt.Println(y_val)
211 | }
212 | 
213 | 214 |

很简单吧,这里利用了Go提供的交叉赋值的功能,另外由于是使用了指针作为参数,所以在swap函数内,x_val和y_val的值就被交换了。

215 | 216 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 雪山飞鹄 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | GolangStudy 2 | =========== 3 | 4 | 用Swift写的Golang学习App。 5 | 6 | 素材及内容来自[https://github.com/jemygraw/GoQuickLearn-iOS](https://github.com/jemygraw/GoQuickLearn-iOS)。 7 | 8 | 感谢[jemygraw](https://github.com/jemygraw) 开源objective-c版本源码。 9 | 10 | 11 | # 知识点 12 | 13 | UITableView、UISegmentedControl、UIWebView控件的使用及使用NSXMLParser解析XML。 14 | 15 | 16 | # Screenshots 17 | 18 | ![Screenshots/golang.gif](Screenshots/golang.gif) 19 | 20 | * iPad真机 21 | 22 | ![Screenshots/27D2524682F39141366C816BF4E6D9F3.png](Screenshots/27D2524682F39141366C816BF4E6D9F3.png) 23 | 24 | ![Screenshots/92A390D999D952485BE806130899F09B.png](Screenshots/92A390D999D952485BE806130899F09B.png) 25 | 26 | ![Screenshots/3BA9A25E9C3A79D65C23DB5ED42BEC35.png](Screenshots/3BA9A25E9C3A79D65C23DB5ED42BEC35.png) 27 | 28 | 29 | # 应用安装 30 | 31 | 目前支持iOS7以上越狱的设备。可通过访问蒲公英应用广场搜索*Go轻松学*或[http://www.pgyer.com/7TU8](http://www.pgyer.com/7TU8)在线安装。 32 | 33 | # License 34 | 35 | MIT 36 | 37 | Copyright (c) 2014 雪山飞鹄 38 | -------------------------------------------------------------------------------- /Screenshots/27D2524682F39141366C816BF4E6D9F3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/v5tech/GolangStudy/0fe20e65f8e2670bb38ba26021c7e928f5897a76/Screenshots/27D2524682F39141366C816BF4E6D9F3.png -------------------------------------------------------------------------------- /Screenshots/3BA9A25E9C3A79D65C23DB5ED42BEC35.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/v5tech/GolangStudy/0fe20e65f8e2670bb38ba26021c7e928f5897a76/Screenshots/3BA9A25E9C3A79D65C23DB5ED42BEC35.png -------------------------------------------------------------------------------- /Screenshots/92A390D999D952485BE806130899F09B.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/v5tech/GolangStudy/0fe20e65f8e2670bb38ba26021c7e928f5897a76/Screenshots/92A390D999D952485BE806130899F09B.png -------------------------------------------------------------------------------- /Screenshots/golang.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/v5tech/GolangStudy/0fe20e65f8e2670bb38ba26021c7e928f5897a76/Screenshots/golang.gif --------------------------------------------------------------------------------