Quantcast
Channel: CSDN博客推荐文章
Viewing all 35570 articles
Browse latest View live

cocos-lua学习笔记(五)cocos2d-Lua类的实现

$
0
0

一、cocos2d-lua继承的代码

打开src->framework->function.lua

**注意:** Lua 继承类覆盖的方法并不能从 C++ 调用到。也就是说通过 C++ 代码调用这个 cc.Node 对象的 setPosition() 方法时,并不会执行我们在 Lua 中定义的 Toolbar:setPosition() 方法。

@param string classname 类名
@param [mixed super] 父类或者创建对象实例的函数

@return table

]]

   function class(classname, super)

    local superType = type(super)
    local cls
--superType获取父类类型,可以使nil、function以及table.
--如果superType 是nil,说明类是独立的,没有父类。
    if superType ~= "function" and superType ~= "table" then
        superType = nil
        super = nil
    end

    if superType == "function" or (super and super.__ctype == 1) then
        -- inherited from native C++ Object 
        cls = {}
        
        --父类如果是个表
        if superType == "table" then
            -- copy fields from super 从父类copy 字段
            for k,v in pairs(super) do cls[k] = v end
            cls.__create = super.__create
            cls.super    = super
        else    --父类是函数
            --把当前类的创建函数,改为父类的函数
            cls.__create = super
            cls.ctor = function() end
        end
        --赋值类名
        cls.__cname = classname
        cls.__ctype = 1
        --创建new方法
        function cls.new(...)
            local instance = cls.__create(...)
            -- copy fields from class to native object
            for k,v in pairs(cls) do instance[k] = v end
            instance.class = cls
            instance:ctor(...)
            return instance
        end
        --创建对象时,可以通过className.new这种方式来创建
        --如 local MySpriteClass = class("MySpriteClass",cc.Sprite)
        --     return MySpriteClass
        --mySpriteClass 实例创建
        --   local mySprite = MySpriteClass.new(xxx.png)
    else
        --父类是lua对象时,cls.__ctype 不为1 superType不是函数
        -- inherited from Lua Object
        if super then
            cls = {}
            --设置cls 元表是父类表
            setmetatable(cls, {__index = super})
            --保存父类表
            cls.super = super
        else
            --nil的情况
            cls = {ctor = function() end}
        end

        cls.__cname = classname
        cls.__ctype = 2 -- lua
        cls.__index = cls

        function cls.new(...)
            local instance = setmetatable({}, cls)
            instance.class = cls
            instance:ctor(...)
            return instance
        end
    end

    return cls
end


二、分析两种继承

举个例子  

print(type(cc.Node))
print(type(cc.Node.create()))

cc.Node是table,但它创建出来的实例对象时userdata,userdata描述C创建的新类型。

由于userdata和table不同,所以需要两种继承。


UserData(用户自定义类型)

意义:使用C语言编写的用于扩展Lua的新类型,方便使用脚本编写或者提高效率

userdata:提供了一块原始的内存区域,用于存储任何东西,在Lua中userdata没有任何预定义操作
生成:void *lua_newuserdata(L,size) 根据指定大小分配一块内存,并将userdata压入栈中,最后返回这个内存块的地址


来自书上的图。


classname.new()生成的instance包含一个class属性。class指向了类原型,并具有super,ctor,__cname和__ctype  4个属性


继承C++的类,new方法使用__create函数来创建实例

继承lua类,new方法使用{ }来创建实例。


继承Lua的类,new方法使用{ }来创建实例。

不是特别理解,书上写的这些东西。

日后有更完善的例子,我就加到这下面。 ~~~~~~~~~~


作者:hiwoshixiaoyu 发表于2017/11/10 14:59:23 原文链接
阅读:24 评论:0 查看评论

第8章 基于UDP套接字编程

$
0
0

客户端:

#include "../Gnet.h"

void do_client(int udpfd, struct sockaddr* pserver_addr, socklen_t server_addr_len)
{
    char buf[MAX_LINE];
    int nread;

    while(fgets(buf, MAX_LINE, stdin) != NULL)
    {
        sendto(udpfd, buf, strlen(buf), 0, pserver_addr, server_addr_len);
        nread = recvfrom(udpfd, buf, MAX_LINE, 0, NULL, NULL);
        fputs(buf, stdout);
    }
}

int main(int argc, const char* argv[])
{
    int udpfd;
    struct sockaddr_in server_addr;

    if(argc < 2)
        perr_exit("usage : client <IPaddress>");

    udpfd = Socket(AF_INET, SOCK_DGRAM, 0);
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(SERVER_PORT);
    inet_pton(AF_INET, argv[1], &server_addr.sin_addr);

    do_client(udpfd, (struct sockaddr*)&server_addr, sizeof(server_addr));

    return 0;
}
#include "../Gnet.h"

void do_client(int udpfd, struct sockaddr* pserver_addr, socklen_t server_addr_len)
{
    char buf[MAX_LINE];
    char reply_ip[INET_ADDRSTRLEN];
    int nread;
    struct sockaddr_in reply_addr;
    socklen_t reply_addr_len;

    reply_addr_len = server_addr_len;
    while(fgets(buf, MAX_LINE, stdin) != NULL)
    {
        sendto(udpfd, buf, strlen(buf), 0, pserver_addr, server_addr_len);
        nread = recvfrom(udpfd, buf, MAX_LINE, 0, (struct sockaddr*)&reply_addr, &reply_addr_len);
        if(reply_addr_len != server_addr_len ||
           memcmp(&reply_addr, pserver_addr, reply_addr_len) != 0)
        {
            inet_ntop(AF_INET, &reply_addr, reply_ip, INET_ADDRSTRLEN);
            printf("reply from %s (ignored)\n", reply_ip);
            continue;
        }
        fputs(buf, stdout);
    }
}

int main(int argc, const char* argv[])
{
    int udpfd;
    struct sockaddr_in server_addr;

    if(argc < 2)
        perr_exit("usage : client <IPaddress>");

    udpfd = Socket(AF_INET, SOCK_DGRAM, 0);
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(SERVER_PORT);
    inet_pton(AF_INET, argv[1], &server_addr.sin_addr);

    do_client(udpfd, (struct sockaddr*)&server_addr, sizeof(server_addr));

    return 0;
}
#include "../Gnet.h"

void do_client(int udpfd, struct sockaddr* pserver_addr, socklen_t server_addr_len)
{
    char buf[MAX_LINE];
    int nread;

    Connect(udpfd, pserver_addr, server_addr_len);
    while(fgets(buf, MAX_LINE, stdin) != NULL)
    {
        Write(udpfd, buf, strlen(buf));
        nread = Read(udpfd, buf, MAX_LINE);
        
        fputs(buf, stdout);
    }
}

int main(int argc, const char* argv[])
{
    int udpfd;
    struct sockaddr_in server_addr;

    if(argc < 2)
        perr_exit("usage : client <IPaddress>");

    udpfd = Socket(AF_INET, SOCK_DGRAM, 0);
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(SERVER_PORT);
    inet_pton(AF_INET, argv[1], &server_addr.sin_addr);

    do_client(udpfd, (struct sockaddr*)&server_addr, sizeof(server_addr));

    return 0;
}



服务器:

#include "../Gnet.h"

void do_server(int udpfd)
{
    ssize_t nread;
    char buf[MAX_LINE];
    struct sockaddr_in client_addr;
    socklen_t client_addr_len;

    while(1)
    {
        client_addr_len = sizeof(client_addr);
        nread = recvfrom(udpfd, buf, MAX_LINE, 0, (struct sockaddr*)&client_addr, &client_addr_len);
        sendto(udpfd, buf, nread, 0, (struct sockaddr*)&client_addr, client_addr_len);
    }
}

int main(int argc, const char* argv[])
{
    int udpfd;
    struct sockaddr_in server_addr;

    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    server_addr.sin_port = htons(SERVER_PORT);

    udpfd = Socket(AF_INET, SOCK_DGRAM, 0);
    Bind(udpfd, (const struct sockaddr*)&server_addr, sizeof(server_addr));
    printf("waiting for connecting.\n");

    do_server(udpfd);

    return 0;
}

服务器(select模型+tcp+udp):

#include "../Gnet.h"

void do_server(int connfd)
{
    ssize_t nread;
    char buf[MAX_LINE];

    while((nread = Read(connfd, buf, MAX_LINE)) > 0)
        Write(connfd, buf, nread);
}

void sig_child(int signo)
{
    pid_t pid;
    int stat;

    printf("in sig_child.\n");
    while((pid = waitpid(-1,&stat, WNOHANG)) > 0)
        printf("child %d terminated\n", pid);
    printf("out sig_child.\n");
}

int main(int argc, const char* argv[])
{
    int lfd, connfd;
    int udpfd;
    struct sockaddr_in server_addr, client_addr;
    socklen_t client_addr_len;
    pid_t child_id;
    const int on = 1;
    fd_set rset;
    int maxfd;
    int nready;
    int nread;
    char buf[MAX_LINE];

    struct sigaction sigaction_set, sigaction_get;
    sigaction_set.sa_handler = sig_child;
    sigemptyset(&sigaction_set.sa_mask);
    sigaction_set.sa_flags = 0;
    if(sigaction(SIGCHLD, &sigaction_set, &sigaction_get) <0)
        printf("sigaction(SIGCHLD) error!\n");

    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    server_addr.sin_port = htons(SERVER_PORT);

    lfd = Socket(AF_INET, SOCK_STREAM, 0);
    setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));//端口复用
    Bind(lfd, (const struct sockaddr*)&server_addr, sizeof(server_addr));
    Listen(lfd, LISTENQ);

    udpfd = Socket(AF_INET, SOCK_DGRAM, 0);
    Bind(udpfd, (struct sockaddr*)&server_addr, sizeof(server_addr));
    printf("waiting for connecting.\n");

    maxfd = lfd > udpfd ? lfd : udpfd;
    FD_ZERO(&rset);
    while(1)
    {
        FD_SET(lfd, &rset);
        FD_SET(udpfd, &rset);

        if((nready = select(maxfd+1, &rset, NULL, NULL, NULL)) < 0)
        {
            if(errno == EINTR)
                continue;
            else
                perr_exit("select error");
        }

        if(FD_ISSET(lfd, &rset))
        {
            client_addr_len = sizeof(client_addr);
            connfd = Accept(lfd, (struct sockaddr*)&client_addr, &client_addr_len);
            if((child_id = fork()) == 0)//子进程
            {
                Close(lfd);
                do_server(connfd);
                Close(connfd);
                exit(0);
            }
            else//父进程
            {
                printf("child %d connected\n", child_id);
                Close(connfd);
            }
        }

        if(FD_ISSET(udpfd, &rset))
        {
            client_addr_len = sizeof(client_addr);
            nread = recvfrom(udpfd, buf, MAX_LINE, 0, (struct sockaddr*)&client_addr, &client_addr_len);
            sendto(udpfd, buf, nread, 0, (struct sockaddr*)&client_addr, client_addr_len);
        }
    }

    return 0;
}

github:https://github.com/gongluck/unp-notes
作者:gongluck93 发表于2017/11/10 17:45:30 原文链接
阅读:17 评论:0 查看评论

LWC 57:723. Candy Crush

$
0
0

LWC 57:723. Candy Crush

传送门:723. Candy Crush

Problem:

This question is about implementing a basic elimination algorithm for Candy Crush.

Given a 2D integer array board representing the grid of candy, different positive integers board[i][j] represent different types of candies. A value of board[i][j] = 0 represents that the cell at position (i, j) is empty. The given board represents the state of the game following the player’s move. Now, you need to restore the board to a stable state by crushing candies according to the following rules:

  • If three or more candies of the same type are adjacent vertically or horizontally, “crush” them all at the same time - these positions become empty.
  • After crushing all candies simultaneously, if an empty space on the board has candies on top of itself, then these candies will drop until they hit a candy or bottom at the same time. (No new candies will drop outside the top boundary.)
  • After the above steps, there may exist more candies that can be crushed. If so, you need to repeat the above steps.
  • If there does not exist more candies that can be crushed (ie. the board is stable), then return the current board.

You need to perform the above rules until the board becomes stable, then return the current board.

Example 1:

Input: [[110,5,112,113,114],[210,211,5,213,214],[310,311,3,313,314],[410,411,412,5,414],[5,1,512,3,3],[610,4,1,613,614],[710,1,2,713,714],[810,1,2,1,1],[1,1,2,2,2],[4,1,4,4,1014]]
Output: [[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[110,0,0,0,114],[210,0,0,0,214],[310,0,0,113,314],[410,0,0,213,414],[610,211,112,313,614],[710,311,412,613,714],[810,411,512,713,1014]]

Explanation:
alt text

Note:

  • The length of board will be in the range [3, 50].
  • The length of board[i] will be in the range [3, 50].
  • Each board[i][j] will initially start as an integer in the range [1, 2000].

思路:
这不就是开心消消乐么,思路很直接,只要根据列和行检测出连续三个以上的块后,记录下来,消去,并继续更新,直到找不到连续三个块为止。

代码如下:

    public int[][] candyCrush(int[][] board) {
        int n = board.length;
        int m = board[0].length;
        boolean[][] next = new boolean[n][m];
        while (getNext(next, board)){
            List<Integer>[] ans = new ArrayList[m];
            for (int i = 0; i < m; ++i){
                ans[i] = new ArrayList<>();
                for (int j = n - 1; j >= 0; --j){
                    if (!next[j][i]) ans[i].add(board[j][i]);
                }
            }
            board = new int[n][m];
            // 重写
            for (int j = 0; j < m; ++j){
                for (int i = 0; i < ans[j].size(); ++i){
                    board[n - 1 - i][j] = ans[j].get(i);
                }
            }
            next = new boolean[n][m];
        }
        return board;
    }

    public boolean getNext(boolean[][] next, int[][] board){
        boolean exist = false;
        // 遍历行        
        int n = board.length;
        int m = board[0].length;

        for (int i = 0; i < n; ++i){
            int prev = board[i][0];
            int count = 1;
            for (int j = 1; j < m; ++j){
                if (board[i][j] == prev && board[i][j] != 0){
                    count ++;
                }   
                else{
                    if (count >= 3){
                        for (int l = 0; l < count; ++l){
                            next[i][j - 1 - l] = true;
                        }
                        exist = true;
                    }
                    count = 1;          
                }
                prev = board[i][j];
            }

            if (count >= 3){
                for (int l = 0; l < count; ++l){
                    next[i][m - 1 - l] = true;
                }
                exist = true;
            }
        }

        // 遍历列
        for (int i = 0; i < m; ++i){
            int prev = board[0][i];
            int count = 1;
            for (int j = 1; j < n; ++j){
                if (board[j][i] == prev && board[j][i] != 0){
                    count ++;
                }
                else{
                    if (count >= 3){
                        for (int l = 0; l < count; ++l){
                            next[j - 1 - l][i] = true;
                        }
                        exist = true;
                    }
                    count = 1;
                }
                prev = board[j][i];
            }

            if (count >= 3){
                    for (int l = 0; l < count; ++l){
                        next[n - 1 - l][i] = true;
                    }
                    exist = true;
            }
        }


        return exist;        
    }
作者:u014688145 发表于2017/11/10 18:00:46 原文链接
阅读:0 评论:0 查看评论

git权威指南总结三:git重置

$
0
0

重置概念

git重置就是将git log中的提交版本回退到前面一个提交版本,下图打印git log的数据显示:

这里写图片描述

可以看到当前处于b17fd1…提交ID,而我们如果想要回退到上一个版本即f5b8c…提交ID所处版本,这时候就需要使用重置命令


重置测试

首先我们进行如下提交
echo "git reset test" > reset.txt
git add reset.txt
git commit -m "git reset test commit"
然后查看commit提交日志

这里写图片描述

在当前工作目录中也自然会存在reset.txt文件,这时候如果我们想要回退到前面一个版本,也就是创建reset.txt文件之前,我们可以使用命令:git reset --hard HEAD^,结果提交日志如下

这里写图片描述

下面将开始git reset的深入理解


重置:git reset的深入理解

重置命令git reset是git最常用的命令之一,同时也是最危险最容易误用的命令,首先来看看它的语法:
1.git reset [-q] [<commit>] [--] <paths>...

2.git reset [--soft| --mixed| --hard| --merge| --keep] [-q] [<commit>]
首先看commit参数,它是可选的,可以使用引用(如HEAD^)或者提交的ID做为值,表示对应的HEAD版本,如果省略则表示使用HEAD的指向作为提交ID(在前面就是使用HEAD^即上一次提交做为提交ID的)
第一种语法的作用是用指定提交状态下的文件来替换掉暂存区中对应的文件,即:git reset head index.txt相当于取消之前之前的git add index.txt命令时改变的暂存区,第一种语法不会重置引用
第二种语法则会重置引用,并且可以根据不同的参数有选择的对暂存区或者工作目录进行重置:
1.使用参数–hard:git三个区域都会发生改变:替换成指定提交状态下的文件
2.使用参数–soft:只会更改引用的指向,暂存区和工作目录不会发生改变(此时进行git commit等同于取消重置)
3.使用参数–mixed(默认):会更改引用的指向和暂存区,不会改变工作区
来具体看看下面命令的作用
git reset:等同于git reset head,默认是更改引用指向和暂存区的,不过这里重置到head相当于没有重置
git reset – index.txt:等同于git reset head index.txt,仅仅将index.txt的改动撤出暂存区,即进行git add index.txt命令的反向操作
git reset –hard HEAD^:重置三个区的修改,回退到head^版本即上一个版本


重置的重置

前面我们说重置命令是最危险的命令,这是因为重置一旦发生,它所重置的提交对应的所有数据都将不复存在,包括ID。而这个时候如果我们想要对重置操作进行重置,该怎么办呢?可以通过reflog来挽救错误的重置
git提供了一个git reflog命令,使用show子命令可以查看当前对版本库进行的操作,它用于查看日志文件,但是和查看日志文件最大不同在于显示的顺序,git reflog的第一条日志显示的是最近的更新操作。
在使用git reflog打印出了版本库的最近提交操作之后,我们依然可以使用git reset进行重置,不过和前面的重置命令有所区别,它的结构:git reset --hard <refname>@{<n>},表示重置到响应的日志位置
git reflog的输出中还提供了一个表达式:@{},表是引用refname之前第n次改变时的sha1哈希值。下面我们来进行git reflog简单的回退重置操作


git reflog测试

首先搭建已重置环境,系列git命令如下:
echo "git reflog test" > ref.txt
git add ref.txt
git commit -m "git reflog test commit"
git reset --hard head^
来看下git reset之前和之后的log日志(主要关注他的提交ID)

这里写图片描述
这里写图片描述

接着调用git reflog查看提交日志git reflog show master | head -5(列出最近五次提交日志)

这里写图片描述

然后我们调用git reset 命令进行回退重置git reset --hard master@{1}

这里写图片描述

最终我们发现,它回到了重置之前的提交


git checkout检出

最后,简单的介绍一下git checkout检出命令的使用,该命令和git reset命令一样,同样会重写工作区,检出命令有三种用法,这里不做解释,简单列出几个常用的检出命令:
git checkout branchName:检出分支,更新head以指向名为branchName的分支(同时更新对应的暂存区和工作区)
git checkout:打印出git三个区域之间的差异,经常用来查看三个区域之间是否有差异,查看具体的差异经常使用git diff [head|–cached]
git checkout head:同上
git checkout – filename:用暂存区中的filename文件覆盖工作区的对应的文件相当于命令git add filename的反向操作
git checlout – .或者git checkout .:用暂存区中的所有文件覆盖工作区的所有文件,这条命令最危险,是不可逆命令,建议谨慎操作
git checkout branch – filename:用branch分支指向的提交中的filename替换暂存区和工作区中相应的文件,当前head指向不会改变
作者:qq_27905183 发表于2017/11/10 11:38:27 原文链接
阅读:28 评论:0 查看评论

python爬虫(19)爬取论坛网站——网络上常见的gif动态图

$
0
0

写在前面的话~

有段时间没有写爬虫相关的文章了,今天抽时间把之前做的一个程序分享给大家。

经常逛A站和B站的人,肯定对一个节目不陌生《网络上常见的GIF动态图

今天就来分享一下,怎么通过爬虫自动的将这些个动作收藏到自己的电脑中(其实这个程序5月份就写好了,一直拖到现在才想起来将它分享出来)。

一.思路分析

按照爬虫的基本规律:
1.找到目标
2.抓取目标
3.处理目标内容,获取有用的信息

1.首先我们的目标是:http://gifcc.com/forum.php    即找动图就上 GIFFCC.COM


这个网站呢,是一个论坛式网站,里面分了几大类,反正试试各种动图。
我们的目标呢,就是找到这(收)些(藏)动(到)图(自)的(己)地(电)址(脑).

2.看一下各个模块的网址,看一下有什么规律

'http://gifcc.com/forum-37-1.html',#其他各种GIF动态图出处 
'http://gifcc.com/forum-38-1.html', #美女GIF动态图出处 
'http://gifcc.com/forum-47-1.html',#科幻奇幻电影GIF动态图出处
'http://gifcc.com/forum-48-1.html',#喜剧搞笑电影GIF动态图出处
'http://gifcc.com/forum-49-1.html',#动作冒险电影GIF动态图出处
'http://gifcc.com/forum-50-1.html'#恐怖惊悚电影GIF动态图出处
对的,没错,如果以游客身份访问,那么各个板块的网址就是这样的形式 http://gifcc.com/forum-XX -1.html
那么每个模块中的内容又有什么规律? 来直接上图:



我们关注的是当前页的网址,以及这个页码数,跳到第二页之后,地址变成:http://gifcc.com/forum-38-2.html
那么也就是说 网址的 规律就是 http://gifcc.com/forum-XX-XX.html
这里注意一点,网站的图片是动态加载的, 只有你往下滑动的时候,下面的图片才会逐渐的显现出来,这个暂且记下


3.每一张动图的所在页面的规律


其实这个没啥规律,但是只要我们找到单个图片的地址,就没啥难处理的了.

二 开工动手


1.获取入口页面内容
即根据传入的URL,获取整个页面的源码
	#仅仅获取页面内容
	def get_html_Pages(self,url):  
		try:   
			#browser = webdriver.PhantomJS(executable_path=r'C:\Python27\Scripts\phantomjs.exe') 
			browser = webdriver.PhantomJS() 
			browser.get(url)
			html = browser.execute_script("return document.documentElement.outerHTML")
			browser.close()
			html=HTMLParser.HTMLParser().unescape(html).decode('utf-8')
			return html
        #捕捉异常,防止程序直接死掉    
		except Exception,e:  
			print u"连接失败,错误原因",e
			return None   
这里我们使用了webdriver以及PhantomJS 这些模块,为什么呢?因为网页是动态加载的,这样可以抓取的数据全一点.
那这里还有个疑问, 为什么没有滑动啊什么的,得到的数据

2.获取页码数
	#获取页码		
	def get_page_num(self,html):

		doc = pq(html)  
		print u'开始获取总页码'
		#print doc('head')('title').text()#获取当前title
		try:
			#如果当前页面太多,超过8页以上,就使用另一种方式获取页码
			if doc('div[class="pg"]')('[class="last"]'):
				num_content= doc('div[class="pg"]')('[class="last"]').attr('href')
				print  num_content.split('-')[1].split('.')[0]
				return num_content.split('-')[1].split('.')[0]
			else:
				num_content= doc('div[class="pg"]')('span')
				return filter(str.isdigit,str(num_content.text()))[0]
		#如果获取页码失败,那么就返回1, 即值获取1页内容	
		except Exception,e:
			print u'获取页码失败'.e
			return '1'

这里的页码处理用到了一个模块pq, 即  PyQuery  
 from pyquery import PyQuery as pq 
采用PyQuery的方式查找我们需要的元素,感觉更好处理一点,挺方便的
同时这里的处理稍微有点意思,如果观察这个页面的话,会发现,每个模块的页码数,在上面和下面都有一个,然后我这里裁取的一下,因为我们只需要一个页码数字即可


3-6 第三步到第六步一起来说
其实就是根据页码数,来进行遍历,获取到每一页的内容
然后得到每一页中的所有图片地址

			print  u'总共有 %d页内容' % int(page_num)
			#3.遍历每一页的内容
			for num in range(1,int(page_num)):
				#4.组装新的url
				new_url = self.url.replace( self.url.split('-')[2],(str(num)+'.html') )
				print u'即将获取的页面是:',new_url
				#5.加载每一页内容,获取gif list 的内容
				items=self.parse_items_by_html(self.get_all_page(new_url))
				print u'在第%d页,找到了%d 个图片内容' % (num,len(items))
				#6.处理每一个元素的内容
				self.get_items_url(items,num)
在进行获取每一页的内容的时候,需要重新组装页面地址。
#4.组装新的url
				new_url = self.url.replace( self.url.split('-')[2],(str(num)+'.html') )
				print u'即将获取的页面是:',new_url
有了新的地址,就可以获取当前页面的内容,并进行数据处理,得到每一张图片的地址列表

#5.加载每一页内容,获取gif list 的内容
				items=self.parse_items_by_html(self.get_all_page(new_url))
				print u'在第%d页,找到了%d 个图片内容' % (num,len(items))
	#解析页面内容,获取gif的图片list
	def parse_items_by_html(self, html):  
		doc = pq(html)  
		print u'开始查找内容msg'     
		return doc('div[class="c cl"]')
在获取到图片列表后,再次解析,获取每一张图片的URL
#解析gif 的list ,处理每一个gif内容
	def get_items_url(self,items,num):
		i=1
		for article in items.items():
			print u'开始处理数据(%d/%d)' % (i, len(items))
			#print article
			self.get_single_item(article,i,num)
			i +=1
	
	#处理单个gif内容,获取其地址,gif 最终地址
	def get_single_item(self,article,num,page_num):
		gif_dict={}
		#每个页面的地址
		gif_url= 'http://gifcc.com/'+article('a').attr('href')
		#每个页面的标题
		gif_title= article('a').attr('title')
		
		#每张图的具体地址
		#html=self.get_html_Pages(gif_url)
		#gif_final_url=self.get_final_gif_url(html)
	 
		gif_dict['num']=num
		gif_dict['page_num']=page_num
		gif_dict['gif_url']=gif_url
		gif_dict['gif_title']=gif_title
		self.gif_list.append(gif_dict)
		data=u'第'+str(page_num)+'页|\t'+str(num)+'|\t'+gif_title+'|\t'+gif_url+'\n'
		self.file_flag.write(data)
在这里,把数据整合一下,为将数据写入数据库做准备

7.将图片存到本地,以及将数据写入数据库

#使用urllib2来获取图片最终地址
	def get_final_gif_url_use_urllib2(self,url):
		try:
			html= urllib2.urlopen(url).read()
			gif_pattern=re.compile('<div align="center.*?<img id=.*?src="(.*?)" border.*?>',re.S)
			return re.search(gif_pattern,html).group(1)
		except Exception,e:
			print u'获取页面内容出错:',e
	#最终处理	存贮数据
	def get_gif_url_and_save_gif(self):
		def save_gif(url,name):
			try:
				urllib.urlretrieve(url, name)
			except Exception,e:
				print '存贮失败,原因:',e
		for i in range(0,len(self.gif_list)):
			gif_dict=self.gif_list[i]
			gif_url=gif_dict['gif_url']
			gif_title=gif_dict['gif_title']
			
			#依然使用webdriver获取最终的gif地址
			final_html=self.get_html_Pages(gif_url)
			gif_final_url=self.get_final_gif_url(final_html)
			#使用另外一种方式(urllib2)获取最终地址
			#gif_final_url=self.get_final_gif_url_use_urllib2(gif_url)
			
			gif_dict['gif_final_url']=gif_final_url
			print u'开始向数据库写入第%d页第%d项数据,并开始存贮图片到本地 ' % (gif_dict['page_num'],gif_dict['num'])
			self.BookTable.insert_one(gif_dict)
			gif_name=self.dir_name+'/'+gif_title+'.gif'
			save_gif(gif_final_url,gif_name)
到这里其实大体的内容已经完成了.

我们能够将这个论坛各个模块的动图都存到本地,同时呢,也将数据放入到了数据库中

三 数据库的筛选

在完成了将数据放入到数据库的之后, 我想着可以直接通过调用数据库,将图片保存
(为什么有这个想法呢,因为我发现如果直接在主程序中存贮图片,它跑的太慢了,不如将数据都放到数据库中,之后专门调用数据库来贮存图片)
但是这里发现一个问题,数据中的内容挺多的,然后发现了好多内容是重复的,因此我们需要对数据库进行去重
关于数据去重的内容,其实我之前的文章已经写过了(写那篇文章的时候,这个爬虫已经完成了呢~)
主要思路是针对某一个元素的数量进行操作,pymongo里面有一个方法是可以统计指定元素的数量的,如果当前元素只有一个,就不管,不是一个元素,就删除
核心代码如下:

for url in collection.distinct('name'):#使用distinct方法,获取每一个独特的元素列表
		num= collection.count({"name":url})#统计每一个元素的数量
		print num
		for i in range(1,num):#根据每一个元素的数量进行删除操作,当前元素只有一个就不再删除
			print 'delete %s %d times '% (url,i)
			#注意后面的参数, 很奇怪,在mongo命令行下,它为1时,是删除一个元素,这里却是为0时删除一个
			collection.remove({"name":url},0)
		for i in  collection.find({"name":url}):#打印当前所有元素
			print i
	print collection.distinct('name')#再次打印一遍所要去重的元素

四 读取数据库中的内容,存贮图片

数据去重之后,再次进行图片的存贮,就方便多了
之后如果图片删除了,也不用重新跑一边,或者说有时候本地图片占地方,那么只用保存有数据库的数据就好了
核心代码如下:

def save_gif(url,name):
	try:
		urllib.urlretrieve(url, name)
	except Exception,e:
		print u'存贮失败,原因:',e
client = pymongo.MongoClient('localhost', 27017) 
print client.database_names()


db = client.GifDB
for table in  db.collection_names():
	print 'table name is ',table
	collection=db[table]

	for item in  collection.find():
		try: 
			if item['gif_final_url']:
				url,url_title= item['gif_final_url'],item['gif_title']
				gif_filename=table+'/'+url_title+'.gif'
				print 'start save %s, %s' % (url,gif_filename)
				save_gif(url,gif_filename)
		except Exception,e:
			print u'错误原因:',e

完整代码

01_get_gif_url.py
#coding: utf-8 
from pyquery import PyQuery as pq  
from selenium import webdriver 
import HTMLParser,urllib2,urllib,re,os

import pymongo

import time
import sys  
reload(sys)  
sys.setdefaultencoding('utf-8')  
class download_gif:
	def __init__(self):
		self.url='http://gifcc.com/forum-38-1.html'
		self.url_list=['http://gifcc.com/forum-37-1.html',#其他各种GIF动态图出处 
		'http://gifcc.com/forum-38-1.html', #美女GIF动态图出处 
		'http://gifcc.com/forum-47-1.html',#科幻奇幻电影GIF动态图出处
		'http://gifcc.com/forum-48-1.html',#喜剧搞笑电影GIF动态图出处
		'http://gifcc.com/forum-49-1.html',#动作冒险电影GIF动态图出处
		'http://gifcc.com/forum-50-1.html'#恐怖惊悚电影GIF动态图出处
		]
		self.choices={'1':u'其他各种GIF动态图出处',
		'2':u'美女GIF动态图出处',
		'3':u'科幻奇幻电影GIF动态图出处',
		'4':u'喜剧搞笑电影GIF动态图出处',
		'5':u'动作冒险电影GIF动态图出处',
		'6':u'恐怖惊悚电影GIF动态图出处'
		}
		
		self.dir_name=u'gif出处'
		self.gif_list=[]
		
		self.connection = pymongo.MongoClient()  
		
		#BookTable.insert_one(dict_data)#插入单条数据  
		#BookTable.insert(dict_data)#插入 字典list 数据 
		
	#获取页面内容,并且加载JS, 通过滚动获取页面更多的元素
	def get_all_page(self,url):
		try:
			#browser = webdriver.PhantomJS(executable_path=r'C:\Python27\Scripts\phantomjs.exe') 
			browser = webdriver.PhantomJS() 
			browser.get(url)
			#time.sleep(3) 
			#页面滚动
			js = "var q=document.body.scrollTop=100000"    
			#for i in range(5):  #调试语句,先暂时不加载太多次数
			for i in range(30):  
				#循环执行下滑页面50次  
				browser.execute_script(js)  
				#加载一次,休息一下  
				time.sleep(1)
				print u'这是第 %d 次划动页面' % i
			# 执行js得到整个页面内容
			html = browser.execute_script("return document.documentElement.outerHTML")
			browser.close()
			html=HTMLParser.HTMLParser().unescape(html)
			return html
		except Exception,e:
			print u'发生错误:',e
	
	#解析页面内容,获取gif的图片list
	def parse_items_by_html(self, html):  
		doc = pq(html)  
		print u'开始查找内容msg'     
		return doc('div[class="c cl"]')
		
	#解析gif 的list ,处理每一个gif内容
	def get_items_url(self,items,num):
		i=1
		for article in items.items():
			print u'开始处理数据(%d/%d)' % (i, len(items))
			#print article
			self.get_single_item(article,i,num)
			i +=1
	
	#处理单个gif内容,获取其地址,gif 最终地址
	def get_single_item(self,article,num,page_num):
		gif_dict={}
		#每个页面的地址
		gif_url= 'http://gifcc.com/'+article('a').attr('href')
		#每个页面的标题
		gif_title= article('a').attr('title')
		
		#每张图的具体地址
		#html=self.get_html_Pages(gif_url)
		#gif_final_url=self.get_final_gif_url(html)
	 
		gif_dict['num']=num
		gif_dict['page_num']=page_num
		gif_dict['gif_url']=gif_url
		gif_dict['gif_title']=gif_title
		self.gif_list.append(gif_dict)
		data=u'第'+str(page_num)+'页|\t'+str(num)+'|\t'+gif_title+'|\t'+gif_url+'\n'
		self.file_flag.write(data)
	
	#通过webdriver获得页面内容后,获得最终地址
	def get_final_gif_url(self,html):
		doc = pq(html) 
		image_content= doc('td[class="t_f"]')
		gif_url= image_content('img').attr('src')
		return gif_url
	
	#使用urllib2来获取图片最终地址
	def get_final_gif_url_use_urllib2(self,url):
		try:
			html= urllib2.urlopen(url).read()
			gif_pattern=re.compile('<div align="center.*?<img id=.*?src="(.*?)" border.*?>',re.S)
			return re.search(gif_pattern,html).group(1)
		except Exception,e:
			print u'获取页面内容出错:',e
	#最终处理	存贮数据
	def get_gif_url_and_save_gif(self):
		def save_gif(url,name):
			try:
				urllib.urlretrieve(url, name)
			except Exception,e:
				print '存贮失败,原因:',e
		for i in range(0,len(self.gif_list)):
			gif_dict=self.gif_list[i]
			gif_url=gif_dict['gif_url']
			gif_title=gif_dict['gif_title']
			
			#依然使用webdriver获取最终的gif地址
			final_html=self.get_html_Pages(gif_url)
			gif_final_url=self.get_final_gif_url(final_html)
			#使用另外一种方式(urllib2)获取最终地址
			#gif_final_url=self.get_final_gif_url_use_urllib2(gif_url)
			
			gif_dict['gif_final_url']=gif_final_url
			print u'开始向数据库写入第%d页第%d项数据,并开始存贮图片到本地 ' % (gif_dict['page_num'],gif_dict['num'])
			self.BookTable.insert_one(gif_dict)
			gif_name=self.dir_name+'/'+gif_title+'.gif'
			save_gif(gif_final_url,gif_name)
		
	#仅仅获取页面内容
	def get_html_Pages(self,url):  
		try:   
			#browser = webdriver.PhantomJS(executable_path=r'C:\Python27\Scripts\phantomjs.exe') 
			browser = webdriver.PhantomJS() 
			browser.get(url)
			html = browser.execute_script("return document.documentElement.outerHTML")
			browser.close()
			html=HTMLParser.HTMLParser().unescape(html).decode('utf-8')
			return html
        #捕捉异常,防止程序直接死掉    
		except Exception,e:  
			print u"连接失败,错误原因",e
			return None   
	
	#获取页码		
	def get_page_num(self,html):

		doc = pq(html)  
		print u'开始获取总页码'
		#print doc('head')('title').text()#获取当前title
		try:
			#如果当前页面太多,超过8页以上,就使用另一种方式获取页码
			if doc('div[class="pg"]')('[class="last"]'):
				num_content= doc('div[class="pg"]')('[class="last"]').attr('href')
				print  num_content.split('-')[1].split('.')[0]
				return num_content.split('-')[1].split('.')[0]
			else:
				num_content= doc('div[class="pg"]')('span')
				return filter(str.isdigit,str(num_content.text()))[0]
		#如果获取页码失败,那么就返回1, 即值获取1页内容	
		except Exception,e:
			print u'获取页码失败'.e
			return '1'
			
		# filter(str.isdigit,num_content)#从字符串中提取数字
		
	#创建文件夹	
	def mk_dir(self,path):
		if not os.path.exists(path):  
			os.makedirs(path) 
			
	def set_db(self,tablename):
		self.BookDB = self.connection.GifDB         #数据库db的名字  
		self.BookTable =self.BookDB[tablename]           #数据库table表的名字  
			
	#主函数		
	def run(self):
		choice_type=5
		if choice_type:
		#for choice_type in range(len(self.choices)): 
			if  choice_type+1:
				
				self.dir_name=self.choices[str(choice_type+1)].strip()
				self.url=self.url_list[int(choice_type)]
				
				
				print self.dir_name,self.url
		
		
			#0.创建文件夹存贮图片,建立文件存贮内容
			self.mk_dir(self.dir_name)
			self.filename=self.dir_name+'/'+self.dir_name+'.txt'
			print self.filename
			self.file_flag=open(self.filename,'w')
			
			self.set_db(self.dir_name)
			self.BookTable .insert({'filename':self.dir_name})
			
			print self.url
			#1.获取入口页面内容
			html=self.get_html_Pages(self.url)
			
			#2.获取页码数目
			page_num=self.get_page_num(html)
			
			print  u'总共有 %d页内容' % int(page_num)
			#3.遍历每一页的内容
			
			#page_num=3#调试语句,先暂时将页面内容设置的小一点
			for num in range(1,int(page_num)):
				#4.组装新的url
				new_url = self.url.replace( self.url.split('-')[2],(str(num)+'.html') )
				print u'即将获取的页面是:',new_url
				#5.加载每一页内容,获取gif list 的内容
				items=self.parse_items_by_html(self.get_all_page(new_url))
				print u'在第%d页,找到了%d 个图片内容' % (num,len(items))
				#6.处理每一个元素的内容
				self.get_items_url(items,num)
			
			#5.数据全部抓取完毕,开始处理数据
			self.get_gif_url_and_save_gif()
			print 'success'
			
			self.file_flag.close()
		
			
if __name__ == '__main__':  
	print u'''
            **************************************************  
            **    Welcome to Spider of  GIF 出处图片        **  
            **         Created on 2017-05-21                **  
            **         @author: Jimy _Fengqi                **  
            ************************************************** 
	'''  		
	print u''' 选择你要下载的gif图片类型
		1:'其他各种GIF动态图出处'
		2:'美女GIF动态图出处'
		3:'科幻奇幻电影GIF动态图出处'
		4:'喜剧搞笑电影GIF动态图出处'
		5:'动作冒险电影GIF动态图出处'
		6:'恐怖惊悚电影GIF动态图出处'
		'''
	#选择要下载的类型	

	mydownload=download_gif()
	html=mydownload.run()
02_delete_repeat_url_in_mongodb.py

#coding: utf-8 
from pyquery import PyQuery as pq  
from selenium import webdriver 
import HTMLParser,urllib2,urllib,re,os

import pymongo

import time
import sys  
reload(sys)  
sys.setdefaultencoding('utf-8')  

import pymongo  

def save_gif(url,name):
	try:
		urllib.urlretrieve(url, name)
	except Exception,e:
		print '存贮失败,原因:',e
		
def print_database_and_table_name():	
	import pymongo
	client = pymongo.MongoClient('localhost', 27017) 
	print client.database_names()

	for database in client.database_names():
		for table in  client[database].collection_names():
			print 'table  [%s]  is in database [%s]' % (table,database)

def delete_single_database_repeat_data():
	import pymongo
	client = pymongo.MongoClient('localhost', 27017) 
	db=client.GifDBtemptemp2#这里是将要清洗数据的数据库名字
	for table in  db.collection_names():
		print 'table name is ',table
		collection=db[table]
		for url in collection.distinct('gif_title'):#使用distinct方法,获取每一个独特的元素列表
			num= collection.count({"gif_title":url})#统计每一个元素的数量
			print num
			for i in range(1,num):#根据每一个元素的数量进行删除操作,当前元素只有一个就不再删除
				print 'delete %s %d times '% (url,i)
				#注意后面的参数, 很奇怪,在mongo命令行下,它为1时,是删除一个元素,这里却是为0时删除一个
				collection.remove({"gif_title":url},0)
			for i in  collection.find({"gif_title":url}):#打印当前所有元素
				print i

def delete_repeat_data():
	import pymongo
	client = pymongo.MongoClient('localhost', 27017) 
	db = client.local
	collection = db.person
	
	for url in collection.distinct('name'):#使用distinct方法,获取每一个独特的元素列表
		num= collection.count({"name":url})#统计每一个元素的数量
		print num
		for i in range(1,num):#根据每一个元素的数量进行删除操作,当前元素只有一个就不再删除
			print 'delete %s %d times '% (url,i)
			#注意后面的参数, 很奇怪,在mongo命令行下,它为1时,是删除一个元素,这里却是为0时删除一个
			collection.remove({"name":url},0)
		for i in  collection.find({"name":url}):#打印当前所有元素
			print i
	print collection.distinct('name')#再次打印一遍所要去重的元素
delete_single_database_repeat_data()

03_from_mongodb_save_pic.py

#coding: utf-8 
from pyquery import PyQuery as pq  
from selenium import webdriver 
import HTMLParser,urllib2,urllib,re,os

import pymongo

import time
import sys  
reload(sys)  
sys.setdefaultencoding('utf-8')  

import pymongo  

def save_gif(url,name):
	try:
		urllib.urlretrieve(url, name)
	except Exception,e:
		print u'存贮失败,原因:',e
client = pymongo.MongoClient('localhost', 27017) 
print client.database_names()


db = client.GifDB
for table in  db.collection_names():
	print 'table name is ',table
	collection=db[table]

	for item in  collection.find():
		try: 
			if item['gif_final_url']:
				url,url_title= item['gif_final_url'],item['gif_title']
				gif_filename=table+'/'+url_title+'.gif'
				print 'start save %s, %s' % (url,gif_filename)
				save_gif(url,gif_filename)
		except Exception,e:
			print u'错误原因:',e
Github地址:https://github.com/JimyFengqi/Gif_Spider

如果感觉博主的内容有帮助,可以支持一下

作者:qiqiyingse 发表于2017/11/10 16:58:21 原文链接
阅读:39 评论:0 查看评论

怎么解决java.lang.NoClassDefFoundError错误

$
0
0

前言

在日常Java开发中,我们经常碰到java.lang.NoClassDefFoundError这样的错误,需要花费很多时间去找错误的原因,具体是哪个类不见了?类明明还在,为什么找不到?而且我们很容易把java.lang.NoClassDefFoundError和java.lang.ClassNotfoundException这两个错误搞混,事实上这两个错误是完全不同的。我们往往花费时间去不断尝试一些其他的方法去解决这个问题,而没有真正去理解这个错误的原因。这篇文章就是通过解决NoClassDefFoundError错误处理的经验分享来揭开NoClassDefFoundError的一些秘密。NoClassDefFoundError的错误并非不能解决或者说很难解决,只是这种错误的表现形式很容易迷惑其他的Java开发者。下面我们来分析下为什么会发生NoClassDefFoundError这样的错误,以及怎样去解决这个错误。

NoClassDefFoundError错误发生的原因

NoClassDefFoundError错误的发生,是因为Java虚拟机在编译时能找到合适的类,而在运行时不能找到合适的类导致的错误。例如在运行时我们想调用某个类的方法或者访问这个类的静态成员的时候,发现这个类不可用,此时Java虚拟机就会抛出NoClassDefFoundError错误。与ClassNotFoundException的不同在于,这个错误发生只在运行时需要加载对应的类不成功,而不是编译时发生。很多Java开发者很容易在这里把这两个错误搞混。

简单总结就是,NoClassDefFoundError发生在编译时对应的类可用,而运行时在Java的classpath路径中,对应的类不可用导致的错误。发生NoClassDefFoundError错误时,你能看到如下的错误日志:

Exception in thread "main" java.lang.NoClassDefFoundError

错误的信息很明显地指明main线程无法找到指定的类,而这个main线程可能时主线程或者其他子线程。如果是主线程发生错误,程序将崩溃或停止,而如果是子线程,则子线程停止,其他线程继续运行。

NoClassDefFoundError和ClassNotFoundException区别

我们经常被java.lang.ClassNotFoundException和java.lang.NoClassDefFoundError这两个错误迷惑不清,尽管他们都与Java classpath有关,但是他们完全不同。NoClassDefFoundError发生在JVM在动态运行时,根据你提供的类名,在classpath中找到对应的类进行加载,但当它找不到这个类时,就发生了java.lang.NoClassDefFoundError的错误,而ClassNotFoundException是在编译的时候在classpath中找不到对应的类而发生的错误。ClassNotFoundException比NoClassDefFoundError容易解决,是因为在编译时我们就知道错误发生,并且完全是由于环境的问题导致。而如果你在J2EE的环境下工作,并且得到NoClassDefFoundError的异常,而且对应的错误的类是确实存在的,这说明这个类对于类加载器来说,可能是不可见的。

怎么解决NoClassDefFoundError错误

根据前文,很明显NoClassDefFoundError的错误是因为在运行时类加载器在classpath下找不到需要加载的类,所以我们需要把对应的类加载到classpath中,或者检查为什么类在classpath中是不可用的,这个发生可能的原因如下:

1.对应的Class在java的classpath中不可用
2.你可能用jar命令运行你的程序,但类并没有在jar文件的manifest文件中的classpath属性中定义
3.可能程序的启动脚本覆盖了原来的classpath环境变量
4.因为NoClassDefFoundError是java.lang.LinkageError的一个子类,所以可能由于程序依赖的原生的类库不可用而导致
5.检查日志文件中是否有java.lang.ExceptionInInitializerError这样的错误,NoClassDefFoundError有可能是由于静态初始化失败导致的
6.如果你工作在J2EE的环境,有多个不同的类加载器,也可能导致NoClassDefFoundError

下面我们看一些当发生NoClassDefFoundError时,我们该如何解决的样例

NoClassDefFoundError解决示例

当发生由于缺少jar文件,或者jar文件没有添加到classpath,或者jar的文件名发生变更会导致java.lang.NoClassDefFoundError的错误。
当类不在classpath中时,这种情况很难确切的知道,但如果在程序中打印出System.getproperty(“java.classpath”),可以得到程序实际运行的classpath
运行时明确指定你认为程序能正常运行的 -classpath 参数,如果增加之后程序能正常运行,说明原来程序的classpath被其他人覆盖了。
NoClassDefFoundError也可能由于类的静态初始化模块错误导致,当你的类执行一些静态初始化模块操作,如果初始化模块抛出异常,哪些依赖这个类的其他类会抛出NoClassDefFoundError的错误。如果你查看程序日志,会发现一些java.lang.ExceptionInInitializerError的错误日志,ExceptionInInitializerError的错误会导致java.lang.NoClassDefFoundError: Could not initialize class,如下面的代码示例:
/**
 * Java program to demonstrate how failure of static initialization subsequently cause
 * java.lang.NoClassDefFoundError in Java.
 * @author Javin Paul
 */
public class NoClassDefFoundErrorDueToStaticInitFailure {

    public static void main(String args[]){

        List<User> users = new ArrayList<User>(2);

        for(int i=0; i<2; i++){
            try{
            users.add(new User(String.valueOf(i))); //will throw NoClassDefFoundError
            }catch(Throwable t){
                t.printStackTrace();
            }
        }         
    }
}

class User{
    private static String USER_ID = getUserId();

    public User(String id){
        this.USER_ID = id;
    }
    private static String getUserId() {
        throw new RuntimeException("UserId Not found");
    }     
}

Output
java.lang.ExceptionInInitializerError
    at testing.NoClassDefFoundErrorDueToStaticInitFailure.main(NoClassDefFoundErrorDueToStaticInitFailure.java:23)
Caused by: java.lang.RuntimeException: UserId Not found
    at testing.User.getUserId(NoClassDefFoundErrorDueToStaticInitFailure.java:41)
    at testing.User.<clinit>(NoClassDefFoundErrorDueToStaticInitFailure.java:35)
    ... 1 more
java.lang.NoClassDefFoundError: Could not initialize class testing.User
    at testing.NoClassDefFoundErrorDueToStaticInitFailure.main(NoClassDefFoundErrorDueToStaticInitFailure.java:23)


Read more: http://javarevisited.blogspot.com/2011/06/noclassdeffounderror-exception-in.html#ixzz3dqtbvHDy

由于NoClassDefFoundError是LinkageError的子类,而LinkageError的错误在依赖其他的类时会发生,所以如果你的程序依赖原生的类库和需要的dll不存在时,有可能出现java.lang.NoClassDefFoundError。这种错误也可能抛出java.lang.UnsatisfiedLinkError: no dll in java.library.path Exception Java这样的异常。解决的办法是把依赖的类库和dll跟你的jar包放在一起。

如果你使用Ant构建脚本来生成jar文件和manifest文件,要确保Ant脚本获取的是正确的classpath值写入到manifest.mf文件
Jar文件的权限问题也可能导致NoClassDefFoundError,如果你的程序运行在像linux这样多用户的操作系统种,你需要把你应用相关的资源文件,如Jar文件,类库文件,配置文件的权限单独分配给程序所属用户组,如果你使用了多个用户不同程序共享的jar包时,很容易出现权限问题。比如其他用户应用所属权限的jar包你的程序没有权限访问,会导致java.lang.NoClassDefFoundError的错误。

基于XML配置的程序也可能导致NoClassDefFoundError的错误。比如大多数Java的框架像Spring,Struts使用xml配置获取对应的bean信息,如果你输入了错误的名称,程序可能会加载其他错误的类而导致NoClassDefFoundError异常。我们在使用Spring MVC框架或者Apache Struts框架,在部署War文件或者EAR文件时就经常会出现Exception in thread “main” java.lang.NoClassDefFoundError。

在有多个ClassLoader的J2EE的环境中,很容易出现NoClassDefFoundError的错误。由于J2EE没有指明标准的类加载器,使用的类加载器依赖与不同的容器像Tomcat、WebLogic,WebSphere加载J2EE的不同组件如War包或者EJB-JAR包。关于类加载器的相关知识可以参考这篇文章类加载器的工作原理。

总结来说,类加载器基于三个机制:委托、可见性和单一性,委托机制是指将加载一个类的请求交给父类加载器,如果这个父类加载器不能够找到或者加载这个类,那么再加载它。可见性的原理是子类的加载器可以看见所有的父类加载器加载的类,而父类加载器看不到子类加载器加载的类。单一性原理是指仅加载一个类一次,这是由委托机制确保子类加载器不会再次加载父类加载器加载过的类。现在假设一个User类在WAR文件和EJB-JAR文件都存在,并且被WAR ClassLoader加载,而WAR ClassLoader是加载EJB-JAR ClassLoader的子ClassLoader。当EJB-JAR中代码引用这个User类时,加载EJB-JAR所有class的Classloader找不到这个类,因为这个类已经被EJB-JAR classloader的子加载器WAR classloader加载。

这会导致的结果就是对User类出现NoClassDefFoundError异常,而如果在两个JAR包中这个User类都存在,如果你使用equals方法比较两个类的对象时,会出现ClassCastException的异常,因为两个不同类加载器加载的类无法进行比较。

有时候会出现Exception in thread “main” java.lang.NoClassDefFoundError: com/sun/tools/javac/Main 这样的错误,这个错误说明你的Classpath, PATH 或者 JAVA_HOME没有安装配置正确或者JDK的安装不正确。这个问题的解决办法时重新安装你的JDK。

Java在执行linking操作的时候,也可能导致NoClassDefFoundError。例如在前面的脚本中,如果在编译完成之后,我们删除User的编译文件,再运行程序,这个时候你就会直接得到NoClassDefFoundError,而错误的消息只打印出User类的名称。

java.lang.NoClassDefFoundError: testing/User
    at testing.NoClassDefFoundErrorDueToStaticInitFailure.main(NoClassDefFoundErrorDueToStaticInitFailure.java:23)

现在我们知道要怎样去面对NoClassDefFoundError异常并解决它了。

转载于:http://blog.csdn.net/jamesjxin/article/details/46606307

作者:qq_36330228 发表于2017/11/10 18:26:10 原文链接
阅读:35 评论:0 查看评论

leetcode题解-344. Reverse String && 541. Reverse String II && 345. Reverse Vowels of a String

$
0
0

今天的三道都是字符串反转的题目,也相对比较简单,我们先来看第一道题目:

Write a function that takes a string as input and returns the string reversed.

Example:
Given s = "hello", return "olleh".

本题没什么好说的就是一个最简单的字符串反转问题,可以自己写程序使用两个指针也可以调用内置函数,两种方法代码如下所示:

    //内置函数
    public String reverseString(String s) {
        StringBuilder res = new StringBuilder(s);
        return res.reverse().toString();
    }

    //双指针法
    public String reverseString1(String s) {
        char[] word = s.toCharArray();
        int i = 0;
        int j = s.length() - 1;
        while (i < j) {
            char temp = word[i];
            word[i] = word[j];
            word[j] = temp;
            i++;
            j--;
        }
        return new String(word);
    }

541、 Reverse String II题目:

Given a string and an integer k, you need to reverse the first k characters for every 2k characters counting from the start of the string. If there are less than k characters left, reverse all of them. If there are less than 2k but greater than or equal to k characters, then reverse the first k characters and left the other as original.
Example:
Input: s = "abcdefg", k = 2
Output: "bacdfeg"
Restrictions:
The string consists of lower English letters only.
Length of the given string and k will in the range [1, 10000]

本题是将字符串中的每2k个字符的前k个进行反转,后k个保持不变,如果最后剩下的不足k个则全部翻转,大于等于k个则将前k个反转。可以使用上面的双指针法来解决此题,先看我的解法:

    //35%
    public static String reverseStr(String s, int k) {
        char [] res = s.toCharArray();
        int n = s.length()/(2*k), i;
        //取前面2k*n个进行反转
        for(i=0; i<n; i++){
            int left=i*2*k, right=left+k-1;
            while(left < right){
                char tmp = res[left];
                res[left] = res[right];
                res[right] = tmp;
                left ++;
                right --;
            }
        }
        //对最后剩余的进行反转,right设置为k和剩余字符串的较小值
        int left=i*2*k, right=Math.min(left+k-1, s.length()-1);
        while(left < right){
            char tmp = res[left];
            res[left] = res[right];
            res[right] = tmp;
            left ++;
            right --;
        }
        return new String(res);
    }

其实仔细一想上面的代码是可以进行简化的,可以把后面的判断语句也融合到前面的循环当中,

    //44%
    public String reverseStr1(String s, int k) {
        char[] arr = s.toCharArray();
        int n = arr.length;
        int i = 0;
        while(i < n) {
            int j = Math.min(i + k - 1, n - 1);
            swap(arr, i, j);
            i += 2 * k;
        }
        return String.valueOf(arr);
    }
    private void swap(char[] arr, int l, int r) {
        while (l < r) {
            char temp = arr[l];
            arr[l++] = arr[r];
            arr[r--] = temp;
        }
    }

然后在理清反转关系之后我们还可以使用StringBuilder内置的insert函数进行简化反转操作:

    //68%
    public String reverseStr2(String s, int k) {
        StringBuilder res = new StringBuilder();
        for (int i = 0; i < s.length(); i++) {
            if (i % (2 * k) < k) res.insert(i - i % (2 * k), s.charAt(i));
            else res.append(s.charAt(i));
        }
        return res.toString();
    }

接下来看最后一道题目:

Write a function that takes a string as input and reverse only the vowels of a string.

Example 1:
Given s = "hello", return "holle".

Example 2:
Given s = "leetcode", return "leotcede".

Note:
The vowels does not include the letter "y".

只反转字符串中的元音“aeiouAEIOU”,有下面三种方法,思路是一样的,都是双指针法,从两侧找元音然后翻转,只是判断元音的数据结构不同,代码的效率也有很大差异,从中我们可以得到下面的结论:
字符串的contains函数效率没有Set效率高,但是这种数据结构的效率都远低于switch语句来的简单方便

    //18%,使用字符串存储元音,然后使用contains函数判断
    public String reverseVowels(String s) {
        if(s == null || s.length()==0) return s;
        String vowels = "aeiouAEIOU";
        char[] chars = s.toCharArray();
        int start = 0;
        int end = s.length()-1;
        while(start<end){

            while(start<end && !vowels.contains(chars[start]+"")){
                start++;
            }

            while(start<end && !vowels.contains(chars[end]+"")){
                end--;
            }

            char temp = chars[start];
            chars[start] = chars[end];
            chars[end] = temp;

            start++;
            end--;
        }
        return new String(chars);
    }

    //55%,使用set存储元音,然后使用contains()函数判断
    public String reverseVowels1(String s) {
        char[] list=s.toCharArray();
        Set<Character> set=new HashSet<>();
        set.add('a');
        set.add('e');
        set.add('i');
        set.add('o');
        set.add('u');
        set.add('A');
        set.add('E');
        set.add('I');
        set.add('O');
        set.add('U');
        for (int i=0, j=list.length-1; i<j; ) {
            if (!set.contains(list[i])) {
                i++;
                continue;
            }
            if (!set.contains(list[j])) {
                j--;
                continue;
            }
            char temp=list[i];
            list[i]=list[j];
            list[j]=temp;
            i++;
            j--;
        }
        return String.valueOf(list);
    }

    public static boolean isVowel(char a){
        switch(a){
            case ('a') : return true;
            case ('e') : return true;
            case ('i') : return true;
            case ('o') : return true;
            case ('u') : return true;
            case ('A') : return true;
            case ('E') : return true;
            case ('I') : return true;
            case ('O') : return true;
            case ('U') : return true;
            default : return false;
        }
    }

    //99%,直接使用switch语句判断是否为元音
    public static String reverseVowels2(String s) {
        if (s.length()<2) return s;

        char[] tab = s.toCharArray();
        int j = tab.length - 1;
        int i = 0;

        while( i < j ) {

            if (!isVowel(tab[i]))
                i++;
            else {
                while (j!=i && !isVowel(tab[j]))
                    j--;

                char temp = tab[i];
                tab[i] = tab[j];
                tab[j] = temp;
                i++;
                j--;
            }
        }
        return new String(tab);
    }
作者:liuchonge 发表于2017/11/10 18:57:42 原文链接
阅读:0 评论:0 查看评论

NOIP2017游记

$
0
0

Day0

早起傻一天,穿了身原谅绿外套,被一群人吐槽

上午写邻接表写挂

中午U盘丢了,翻书包找了好久,结果插在学校机房电脑上

下午去合一报道,乱七八糟七糟八糟

在这里要吐槽合一,每次承办活动都是乱成一团2333......

就不如我们合六的效率高啦[笑]

然后试机的时候rp全程掉线

貌似只有我们学校冒充普及组在不正确的时间试机(雾)结果被一群初中生(小学生?)轰了出来

写了个对拍的板子,各种不顺,linux下我还加文件后缀名.exe,简直zz

机子各种玄学错误,重启到飞起

座位左边金牌爷wyx,右边铜牌大佬rky,和安师大附中在同一个机房考试也是压力巨大

mfs被两位金牌爷( wyx && zzt )包围,全程和大爷谈笑风生

期望rp守恒定律明天能实现

作者:qwerty1125 发表于2017/11/10 19:09:19 原文链接
阅读:31 评论:0 查看评论

快速搭建 webpack + react 环境

$
0
0

安装

首先你需要点击这里安装 nodejs(npm)。然后执行:

建立一个目录作为项目根目录并初始化:

mkdir react-webpack
cd react-webpack/
npm init

安装相关组件

这里包括了本文所需要的全部组件

npm i --save-dev react react-dom react-transform-hmr webpack webpack-dev-server babel-core babel-loader babel-preset-react babel-preset-es2015 babel-plugin-react-transform css-loader style-loader less less-loader react-transform-catch-errors redbox-react --registry=https://registry.npm.taobao.org

下面简单说明上述组件功能

  1. react: react基础组件
  2. react-dom: react 操作 DOM 组件
  3. react-transform-hmr: hot module reloading 为热替换依赖插件
  4. webpack: webpack 基础组件
  5. webpack-dev-server: webpack 服务器组件
  6. babel-core: babel 核心组件
  7. babel-loader: 转码工具
  8. babel-preset-react: 支持 react 转码
  9. babel-preset-es2015: 支持 ES6 转码
  10. babel-plugin-react-transform: 实现 babel 热替换
  11. css-loader: 对 css 文件进行打包
  12. style-loader: 将样式添加进 DOM 中
  13. less: less 语法支持
  14. less-loader: 对 less 文件进行打包
  15. react-transform-catch-errors: 将错误显示在浏览器中
  16. redbox-react: 渲染插件,配合上一个使用显示错误

hello world

建立如下目录结构

其中 webpack.config.js 内容如下

var path = require('path');
var webpack = require('webpack');

module.exports = {
  devtool: 'eval-source-map',   //开启 soursemap
  entry: path.resolve(__dirname, './src/index.js'),  //指定入口
  output: {      //设置输出路径
    path: path.resolve(__dirname, './build'),
    filename: "index.js"
  },
  module: {    //设置 babel 模块
    loaders: [{
      test: /\.(js|jsx)$/,
      exclude: /node_modules/,
      loader: "babel-loader"
    }]
  },
  plugins: [    //加载插件
    new webpack.HotModuleReplacementPlugin() //热模块替换插件
  ]
};

其中 .babelrc 内容如下:

{
  "presets": [
    "react",
    "es2015"
  ]
}

其中 src/index.js 内容如下:

import React from 'react';
import ReactDOM from 'react-dom';

ReactDOM.render(
  <h1>Hello World</h1>,
  document.getElementById('root')
);

其中 build/index.html 内容如下:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8" />
  <title>demo</title>
</head>
<body>
  <div id="root"></div>
</body>
<script src="index.js"></script>
</html>

修改 package.json 中的 scripts 部分如下:

"scripts": {
  "build": "webpack"
}

而后运行 ‘npm run build’ 运行在本地 ‘./build/index.html’ 看到渲染的页面

服务器环境配置

修改或添加 webpack.config.js 中以下部分:

entry: ['webpack/hot/dev-server', path.resolve(__dirname, './src/index.js')],  //指定入口
devServer: {   //配置本地服务器
  contentBase: './build',
  colors: true,
  historyApiFallback: true,
  inline: false,
  port: 4444,
  process: true
}

修改 package.json 中的 scripts 部分如下:

“`js
“scripts”: {
“build”: “webpack”,
“dev”: “webpack-dev-server”
}

而后运行 ‘npm run dev’ 运行在本地 ‘http://localhost:4444/’ 看到渲染的页面

配置 css 和 less

在 ./src/ 中添加 index.less 和 font.css,分别写入以下内容测试功能

“`css
/* index.less */
h1 {
background-color: red;
}
/* font.css */
h1 {
color: yellow;
}

作者:Faremax 发表于2017/11/10 19:22:58 原文链接
阅读:1 评论:0 查看评论

HDU4738 Caocao's Bridges

$
0
0

双连通分量

题目传送门

明天就考试了QAQ

题目大意:给你一张无向图,问图中权值最小的桥的权值。

据说NOIp会考边双。。。赶紧学一下。

边双裸题啊,Tarjan直接缩,如果有两个及以上的连通块就直接输出-1.

注意当桥的权值为0时答案仍然是1,因为仍然需要派一个人去炸桥。

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#define MAXN 1000
using namespace std;
struct edge{
    int next,to,dis;
}ed[MAXN*MAXN*2+5];
int n,m,k,p,num,ans;
int h[MAXN+5],dfn[MAXN+5],low[MAXN+5];
bool f[MAXN+5];
void addedge(int x,int y,int z){
    ed[k].next=h[x]; ed[k].to=y; ed[k].dis=z; h[x]=k++;
}
void Tarjan(int x,int e){
    dfn[x]=low[x]=++p;
    for (int i=h[x];~i;i=ed[i].next)
        if (i!=e){
            int v=ed[i].to;
            if (!dfn[v]){
                Tarjan(v,i^1);
                low[x]=min(low[x],low[v]);
                if (low[v]>dfn[x]) 
                    ans=min(ans,ed[i].dis);
            }
            else low[x]=min(low[x],dfn[v]);
        }
    num++;
}
int main(){
    while (scanf("%d%d",&n,&m)){
        if (n==0&&m==0) break;
        memset(h,-1,sizeof(h)); k=0;
        for (int i=1;i<=m;i++){
            int u,v,d;
            scanf("%d%d%d",&u,&v,&d);
            addedge(u,v,d); addedge(v,u,d);
        }
        p=0; ans=0x7fffffff; num=0;
        memset(dfn,0,sizeof(dfn));
        memset(low,0,sizeof(low));
        Tarjan(1,-1);
        if (num<n) printf("0\n");
        else if (ans==0x7fffffff) printf("-1\n");
        else if (ans==0) printf("1\n");
        else printf("%d\n",ans);
    }
    return 0;
}
作者:a1799342217 发表于2017/11/10 20:32:46 原文链接
阅读:20 评论:0 查看评论

【洛谷3368】树状数组 2 树状数组+差分

$
0
0

树状数组 2

题目描述

如题,已知一个数列,你需要进行下面两种操作:

1.将某区间每一个数数加上x

2.求出某一个数的和

输入输出格式

输入格式:

第一行包含两个整数NM,分别表示该数列数字的个数和操作的总个数。

第二行包含N个用空格分隔的整数,其中第i个数字表示数列第i项的初始值。

接下来M行每行包含24个整数,表示一个操作,具体如下:

操作1格式:1 x y k 含义:将区间[x,y]内每个数加上k

操作2格式:2 x 含义:输出第x个数的值

输出格式:

输出包含若干行整数,即为所有操作2的结果。

输入输出样例

输入样例#1 

5 5
1 5 4 2 3
1 2 4 2
2 3
1 1 5 -1
1 3 5 7
2 4

输出样例#1 

6
10

说明

时空限制:1000ms,128M

数据规模:

对于30%的数据:N<=8M<=10

对于70%的数据:N<=10000M<=10000

对于100%的数据:N<=500000M<=500000

 

树状数组的本职工作是单点修改和区间查询。可这道题却是区间修改单点查询,并非树状数组本职,强做的话只有30分qwq。所以我们想到了差分。我们可以用树状数组来维护一个差分序列。差分序列的本质是通过前缀和使区间修改转换为单点修改。所以在查询的时候只要输出前缀和就可以了。

 

#include<iostream>
#include<cstdio>
#define N 505000
using namespace std;

int n,m;
int c[N];

int lowbit(int x){return x&-x;}
int add(int x,int d)
{
    while(x<=n)
    {
        c[x]+=d;
        x+=lowbit(x);
    }
}
int query(int x)
{
    int ret=0;
    while(x)
    {
        ret+=c[x];
        x-=lowbit(x);
    }
    return ret;
}

int main()
{
    int d;
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++)
    scanf("%d",&d),add(i,d),add(i+1,-d);
    for (int i=0;i<m;i++)
    {
        int order;
        scanf("%d",&order);
        if (order == 1)
        {
            int l,r,k;
            scanf("%d%d%d",&l,&r,&k);
            add(l,k);add(r+1,-k);
        }
        else
        {
            int x;
            scanf("%d",&x);
            printf("%d\n",query(x));
        }
    }
    return 0;
}

 

作者:Dadatu_Zhao 发表于2017/11/10 12:04:43 原文链接
阅读:15 评论:0 查看评论

Java for Web学习笔记(八六):消息和集群(1)一般性了解

$
0
0

应用消息:紧耦合和松耦合

应用消息常用的是RPC和发布/订阅,其实就是分布代表了同步和异步。随着大系统的发展,微服务架构的兴起,松耦合的发布订阅模式越来越流行。

以员工系统为例,涉及财务报税服务,报销服务,工资单服务,福利服务等等,新的服务可能不断出现。如果采用紧耦合的RPC,则员工系统必须对服务了解,并主动调用这些服务,每新增一个服务,员工系统都要进行update。随着新服务的增加,员工系统就越来越复杂,业务逻辑越来越庞大,代码的维护、测试愈困难,任何一个服务出现问题,都会影响员工系统的运作。

社交网络则采用发布订阅方式。发布者无需向每个订阅者逐个发布信息,甚至不关心有没有订阅者。这种解耦的系统会更为简单和易于维护和测试。

发布订阅模式

发布订阅模式有三个角色:发布者(publisher)、订阅者(subscriber)和代理(broker)。以微博为例,微博就是broker,用户即是publisher也是subcriber。broker实现对消息的分派,其本质就是对消息队列的管理。有不少优秀的开源broker项目。后面将学习使用AMQP(Advanced Message Queuing Protocol)的实现RabbitMQ server作为broker。

集群

集群是个很大的topic。

支持分布:<distributable />

一个J2EE应用被认为是分布式的,要同时满足下面的情况:

  1. web.xml中带有<distributable />
  2. 引用的jar包,如果包含了web-fragment.xml,则也必须带有<distributable />;如果没有web-fragment.xml,这个jar包不属于web片段,也就不管是否支持分布式了。

我们看一下log4j-web的jar包:


在JVM之间共享HttpSession对象对分布式的集群是重要的。<distributable />表示应用将用于部署在多个JVM上,要求HttpSession的attributes是Serializable,也即HttpSession可以在不同的JVM之间传递。如果我们设置了<distributable />,而在HttpSession中加入了一个非序列化的属性,则tomcat会抛出IllegalArgumentException异常。

序列化

Java对象通过序列化,可以在集群之间传递。关于序列化,我们在后面有专门的学习。HttpSession是否能够序列化,可以通过javax.servlet.http.HttpSessionActivationListener来监听。
@WebListener
public class TempSessionListerner implements HttpSessionActivationListener {

    public TempSessionListerner() {
        // TODO Auto-generated constructor stub
    }

   /** 表示不适合序列化,只能从其他JVM中接收。对于不合适序列化的参数,标识为transient(不参加序列化),例如文件描述符,数据库连接等
     * 在此处接收后,重新创建或者获取本地对象。   */
    public void sessionDidActivate(HttpSessionEvent se)  { 
         // TODO Auto-generated method stub
    }

   /** 表示适合序列化,可以传递到其他JVM。   */
    public void sessionWillPassivate(HttpSessionEvent se)  { 
         // TODO Auto-generated method stub
    }    
}

在不同的应用之间共享HttpSession,导致每次HttpSession发生变化时,都必须序列化,重新共享,这很可能会引发性能问题。因此,对于集群,倾向于session stickiness(会话粘滞),也就是同一个session,发给同一app处理,HttpSession的共享只是周期性进行。但是如果配置session stickiness,不同的container或者load balance有不同的配置方式,而周期长度的设置取决于开放团队的经验。但是,这也是有漏洞的,如果server down掉了,就可能出现session状态不同步。

设置Container

不同容器配置不一样,如果使用GlassFish Domain Administration Server (DAS),那我们就只需要设置<distributable/>。然而tomcat配置就复杂很多,其缺省配置不支持集群。除了多个容器组成集群外,我们可以加上load banlance,常采用下图的两种组网方式。

左图在传统的企业网常见,Apache HTTPD运行Apache Tomcat Connector,可以将请求路由到负载最轻的Tomcat server。但不支持WebSocket。右图将智能负载均衡有load balance来进行,虽然不能像Apache Tomcat Connector了解那么详细的节点性能,但是优秀的负载均衡器可仍可以利用简单的定期health-check请求了检查server。

将Tomcat server加入集群,最简单的方式是在server.xml中的Engine和Host配置中增加:

<Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"/>

详细还是要去看看tomcat集群设置的资料。

消息在集群中传递

几个使用场景例子:

  1. 在大的集群中通过消息传递update的内容。
  2. 跨机器的消息传递
  3. 对于后台运行的任务,通过消息可以传递其运行状态,只要访问集群中任一个server都可以获得当前状态。

我们需要注意到,消息在集群中传递是异步的,且时间不同,甚至差别较大。

Java Message Service 2.0 (JSR 343)

Java Message Serivce是工业级的消息服务,最早在2001年引入,在2013年发布了JMS 2.0。JMS是API,不是协议,因此虽然代码移植很方便,但是互联互通有问题,而且限于Java语言。

Advanced Message Queuing Protocol

AMQP不是API,是协议,定义了网络传输中packet的结构。其官网是这样定义的[1]

The Advanced Message Queuing Protocol (AMQP) is an open standard for passing business messages between applications or organizations. It connects systems, feeds business processes with the information they need and reliably transmits onward the instructions that achieve their goals.

AMQP最早在2003年,与2013年发布1.0,目前被广泛地应用。Spring framework没有内置AMQP,但是Spring有AMQP项目,可以帮助我们在spring framework中使用AMQ,但我们后面的例子将直接使用AMQP的 Java Client library。


相关链接: 我的Professional Java for Web Applications相关文章

作者:flowingflying 发表于2017/11/10 21:27:36 原文链接
阅读:20 评论:0 查看评论

Java for Web学习笔记(八七):消息和集群(2)应用内的publish和subscribe

$
0
0

学习内容

Spring框架很强大,已经自带app内部的消息发布订阅了。我们甚至不需要在配置文件(代码)中加入任何内容。 我们将学习如何在一个spring应用内进行消息的发布和订阅

定义事件

事件需继承Spring的ApplicationEvent。在发布事件后,broker(spring framework)将发送给订阅的用户。如果订阅了这类事件,相关的子类事件也会监听到。下面是一个例子:TopEvent <- CenterEvent;TopEvent <- MiddleEvent <- BottomEvent。

public class TopEvent extends ApplicationEvent{
    private static final long serialVersionUID = 1L;
    public TopEvent(String event) {
        super(event);
    }
}

public class CenterEvent extends TopEvent{
    private static final long serialVersionUID = 1L;
    public CenterEvent(String event) {
        super(event);
    }
}

public class MiddleEvent extends TopEvent {
    ... ...
}

public class BottomEvent extends MiddleEvent{
    ... ...
}

如果我们订阅了一个TopEvent,将会监听到TopEvent,CenterEvent,MiddleEvent和BottomEvent;如果订阅了MiddleEvent,将会监听到MiddleEvent和BottomEvent,如果订阅了CenterEvent,则监听CenterEvent。可以说Spring是根据类型匹配来确定订阅者的,而发送给订阅者的属性是从最匹配的发送开始,不过根据发布/订阅模式松耦合的开发特性,这个顺序没有意义。

我们再看一个小例子,监听用户login事件和logout事件,而这两个时间都属于认证事件。

//在这个小例子中,我们重点看看一个小技巧,AuthenticationEvent是LoginEvent和LogoutEvent的基类,但是我们并不希望作为一个事件发布,采用了abstract,也就是发布者必须要具体给出是login还是logout事件。
public abstract class AuthenticationEvent extends ApplicationEvent{
    private static final long serialVersionUID = 1L;
    public AuthenticationEvent(Object source) {
        super(source);
    }    
}

public class LoginEvent extends AuthenticationEvent{
    private static final long serialVersionUID = 1L;
    public LoginEvent(String username) {
        super(username);
    }
}

public class LogoutEvent extends AuthenticationEvent{
    ... ...
}

发布事件

@Controller
public class HomeController {
    private static final Logger log = LogManager.getLogger();
    //1】inject spring的publisher
    @Inject ApplicationEventPublisher publisher;

    @RequestMapping("")
    public String login(HttpServletRequest request){
        log.info("Publish LOGIN event");
        //2】发布事件
        this.publisher.publishEvent(new LoginEvent(request.getRemoteAddr()));
        return "login";
    }

    @RequestMapping("/logout")
    public String logout(HttpServletRequest request){
        log.info("Publish LOGOUT event");
        //2】发布事件
        this.publisher.publishEvent(new LogoutEvent(request.getRemoteAddr()));
        return "logout";
    } 
}

订阅事件

订阅某个事件

//订阅某个事件很简单,只要实现ApplicationListener<xxxEvent>即可,由于我们使用的是Spring的发布/订阅,因此必须要在spring框架内,类需要加上@Component,此处,使用了@Service
@Service
public class AuthenticationInterestedParty implements ApplicationListener<AuthenticationEvent>{
    private static final Logger log = LogManager.getLogger();

    @Override
    public void onApplicationEvent(AuthenticationEvent event) {
        log.info("Authentication event for IP address {}.", event.getSource());        
    }
}

同样的,我们可以通过public class LoginInterestedParty implements ApplicationListener<LoginEvent>{...}监听LoginEvent。我们看看log输出:

16:18:21.479 [http-nio-8080-exec-3] [DEBUG] (Spring) DispatcherServlet - DispatcherServlet with name 'springWebDispatcher' processing GET request for [/chapter18/]
16:18:21.479 [http-nio-8080-exec-3] [DEBUG] (Spring) RequestMappingHandlerMapping - Looking up handler method for path /
16:18:21.479 [http-nio-8080-exec-3] [DEBUG] (Spring) RequestMappingHandlerMapping - Returning handler method [public java.lang.String cn.wei.chapter18.site.publish_subscribe.HomeController.login(javax.servlet.http.HttpServletRequest)]
16:18:21.479 [http-nio-8080-exec-3] [DEBUG] (Spring) DefaultListableBeanFactory - Returning cached instance of singleton bean 'homeController'
16:18:21.479 [http-nio-8080-exec-3] [DEBUG] (Spring) DispatcherServlet - Last-Modified value for [/chapter18/] is: -1
16:18:21.512 [http-nio-8080-exec-3] [INFO ] HomeController:24 login() - Publish LOGIN event
16:18:21.512 [http-nio-8080-exec-3] [DEBUG] (Spring) DefaultListableBeanFactory - Returning cached instance of singleton bean 'authenticationInterestedParty'
16:18:21.512 [http-nio-8080-exec-3] [DEBUG] (Spring) DefaultListableBeanFactory - Returning cached instance of singleton bean 'loginInterestedParty'
16:18:21.512 [http-nio-8080-exec-3] [INFO ] AuthenticationInterestedParty:14 onApplicationEvent() - Authentication event for IP address 0:0:0:0:0:0:0:1.
16:18:21.512 [http-nio-8080-exec-3] [INFO ] LoginInterestedParty:16 onApplicationEvent() - Login event for IP address 0:0:0:0:0:0:0:1.

可以看到发布和订阅者的处理以及servlet的处理是在同一个线程的。在发布/订阅模式中,发布者并不理会订阅者会如何处理,如果订阅者的处理时间很长,就必定会影响发布者正常servlet的处理。因此,订阅者更适合采用异步处理方式。

@Service
public class LogoutInterestedParty implements ApplicationListener<LogoutEvent>{
    private static final Logger log = LogManager.getLogger();

    @Override
    @Async
    public void onApplicationEvent(LogoutEvent event) {
        log.info("Logout event for IP address {}.", event.getSource());
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
        }
        log.info("Logout done");
    }
}

输出为(将spring的输出级别上调为INFO):

16:25:17.440 [http-nio-8080-exec-6] [INFO ] AuthenticationInterestedParty:14 onApplicationEvent() - Authentication event for IP address 0:0:0:0:0:0:0:1.
16:25:17.519 [task-1] [INFO ] LogoutInterestedParty:16 onApplicationEvent() - Logout event for IP address 0:0:0:0:0:0:0:1.
16:25:20.521 [task-1] [INFO ] LogoutInterestedParty:21 onApplicationEvent() - Logout done

订阅多个事件

在实际应用中,很可能一个Service订购了好几个事件,但是Java是不能多次实现某个接口的:

public class MyService implements ApplicationListener<OneEvent>,ApplicationListener<TwoEvent>{} //这是不允许的

我们可以采用下面的方式:

/**
 * 这里演示如何在同一个类中(某个服务中),采用内部类的方式,订阅多个事件。   
 * 在Spring框架中,要求某个实例作为Spring的框架管理,需要添加@Bean的标识,如同我们在配置文件的做法。
 * 我们将Spring ApplicationListener的具体实现(采用内部类)作为@Bean在此标记,纳入Spring框架中,正如作为的类时采用的@Component
 */
@Service
public class EventService {
    private static final Logger logger = LogManager.getLogger();

    @Bean
    public ApplicationListener<BottomEvent> bottomEventListener(){
        return new ApplicationListener<BottomEvent>(){
            @Async
            public void onApplicationEvent(BottomEvent event) {
                logger.info("Bottom event for {}.", event.getSource());
            };
        };        
    }

    @Bean
    public ApplicationListener<CenterEvent> centerEventListener(){
        return new ApplicationListener<CenterEvent>(){
            @Async
            public void onApplicationEvent(CenterEvent event) {
                logger.info("Center event for {}.", event.getSource());
            };
        };        
    }

    @Bean
    public ApplicationListener<MiddleEvent> middleEventListener(){
        return new ApplicationListener<MiddleEvent>(){
            @Async
            public void onApplicationEvent(MiddleEvent event) {
                logger.info("Middle event for {}.", event.getSource());
            };
        };        
    }

    @Bean
    public ApplicationListener<TopEvent> topEventListener(){
        return new ApplicationListener<TopEvent>(){
            @Async
            public void onApplicationEvent(TopEvent event) {
                logger.info("Top event for {}.", event.getSource());
            };
        };        
    }
}

相关链接: 我的Professional Java for Web Applications相关文章

作者:flowingflying 发表于2017/11/10 21:47:31 原文链接
阅读:9 评论:0 查看评论

Ubuntu Server 1404 换源

$
0
0


换源 先备份

cp /etc/apt/sources.list /etc/apt/sources.list bak


备份完后

sudo vi /etc/apt/sources.list  
没有 vii  vim  也可以


阿里的有点bug cn 是可以的


##Ubuntu 官方更新服务器(欧洲,此为官方源,国内较慢,但无同步延迟问题,电信、移动/铁通、联通等##公网用户可以使用):
deb http://archive.ubuntu.com/ubuntu/ trusty main restricted universe multiverse 
deb http://archive.ubuntu.com/ubuntu/ trusty-security main restricted universe multiverse 
deb http://archive.ubuntu.com/ubuntu/ trusty-updates main restricted universe multiverse 
deb http://archive.ubuntu.com/ubuntu/ trusty-proposed main restricted universe multiverse 
deb http://archive.ubuntu.com/ubuntu/ trusty-backports main restricted universe multiverse 
deb-src http://archive.ubuntu.com/ubuntu/ trusty main restricted universe multiverse 
deb-src http://archive.ubuntu.com/ubuntu/ trusty-security main restricted universe multiverse 
deb-src http://archive.ubuntu.com/ubuntu/ trusty-updates main restricted universe multiverse 
deb-src http://archive.ubuntu.com/ubuntu/ trusty-proposed main restricted universe multiverse 
deb-src http://archive.ubuntu.com/ubuntu/ trusty-backports main restricted universe multiverse

##国内默认源
deb http://cn.archive.ubuntu.com/ubuntu/ trusty main restricted universe multiverse
deb http://cn.archive.ubuntu.com/ubuntu/ trusty-security main restricted universe multiverse
deb http://cn.archive.ubuntu.com/ubuntu/ trusty-updates main restricted universe multiverse
deb http://cn.archive.ubuntu.com/ubuntu/ trusty-backports main restricted universe multiverse
##測試版源
deb http://cn.archive.ubuntu.com/ubuntu/ trusty-proposed main restricted universe multiverse
# 源碼
deb-src http://cn.archive.ubuntu.com/ubuntu/ trusty main restricted universe multiverse
deb-src http://cn.archive.ubuntu.com/ubuntu/ trusty-security main restricted universe multiverse
deb-src http://cn.archive.ubuntu.com/ubuntu/ trusty-updates main restricted universe multiverse
deb-src http://cn.archive.ubuntu.com/ubuntu/ trusty-backports main restricted universe multiverse
##測試版源
deb-src http://cn.archive.ubuntu.com/ubuntu/ trusty-proposed main restricted universe multiverse
# Canonical 合作夥伴和附加
deb http://archive.canonical.com/ubuntu/ trusty partner
deb http://extras.ubuntu.com/ubuntu/ trusty main


deb http://mirrors.aliyun.com/ubuntu/ trusty main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ trusty-security main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ trusty-updates main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ trusty-proposed main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ trusty-backports main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ trusty main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ trusty-security main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ trusty-updates main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ trusty-proposed main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ trusty-backports main restricted universe multiverse


换源后  执行

sudo apt -get update

在更新中 强制退出 的话 

再一次apt-get 时  提示 unable 什么的  把那么文件删掉  就可以

作者:sizaif 发表于2017/11/10 21:59:44 原文链接
阅读:0 评论:0 查看评论

Spring与Hibernate整合

$
0
0

本章简介

1章、第2章中分别介绍了 Spring的控制反转和面向切面编程,我们了解了依赖注入的含义、依赖注入的方式、面向切面的编程以及4种通知。本章先介绍Spring与Hibernate整合的目的,然后采用对Emp表的CURD作为综合案例,介绍两大框架整合的步骤、关键整合点、整合之后如何操作数据库。最后采用Spring的声明式事务对Hibernate进行管理,增加程序的健壮性。

核心技能部分

第1章 

1.1 SpringHibernate整合简介 

当我们用JDBC编程的时候,数据库操作的代码非常繁琐,后来学习Hibernate,数据库的操作代码大大简化,但代码仍然比较繁琐,我们可以充分使用Spring依赖注入和AOP简化Hibernate应用。

Spring在资源管理、DAO 的实现及事务策略方面提供与Hibernate 的集成,通过IOC对Hibernate提供一流的支持,并在解决典型的 Hibernate整合问题中展现了突出的作用。Spring提供的支持都遵循其通用的事务及DAO异常体系。

在实际应用开发中,web程序的依赖关系是这样的:Action依赖Biz,Biz依赖DAO,DAO依赖SessionFactory,SessionFactory依赖DataSource。可以使用Spring的依赖注入来管理这些对象。事务处理在实际应用中非常重要,如果在每个涉及数据库操作的方法中都加入事务控制代码,那将是一件很痛苦的事情,而Spring AOP可以很好的解决这个问题。


Spring 向不同的事务 API提供一致的编程模型,提供比传统事务 API更简单且易用的编程式事务管理 API,能够整合数据访问,支持声明式事务管理。

1.1 Spring与Hibernate整合

我们以操作emp表为例,来探索两大框架的整合过程。

1.1.1 处理SessionFactory 

Spring与Hibernate集成开发主要包括两种方式,二者的区别在于配置文件的使用。

1.将Spring与Hibernate各自的配置文件进行合并

两种方式都可以使用MyEclipse工具完成,操作步骤如下:

步骤1 添加Spring特性,如图4.1.2所示。


步骤2 勾选Spring支持包,如图4.1.3所示。










步骤6 把Hibernate配置放入现有的Spring配置文件,生成sessionFactory,如图4.1.7所示。




步骤7 确定数据库信息,完成配置,如图4.1.8所示。



2. Spring与Hibernate分别使用各自的配置文件

当需要将Spring的配置文件与Hibernate配置文件分开配置的时候,只有步骤5和步骤6不一样,分别将两个步骤替换如下:

步骤5 勾选单独的Hibernate配置文件,如图4.1.9所示。




步骤6 生成Hibernate配置文件和sessionFactory,如图4.1.10所示。




合并后的配置文件如示例4.1所示。

示例4.1


<!-- dataSource的配置 -->

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">

<property name="driverClassName" value="oracle.jdbc.OracleDriver">

</property>

<property name="url" value="jdbc:oracle:thin:@127.0.0.1:1521:orcl">

</property>

<property name="username" value="scott"></property>

<property name="password" value="tiger"></property>

</bean>

<!-- sessionFactory的配置 -->

<bean id="sessionFactory"

class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">

<property name="dataSource">

<ref bean="dataSource" />

</property>

<property name="hibernateProperties">

<props>

<prop key="hibernate.dialect">

org.hibernate.dialect.Oracle9Dialect

</prop>

<!—显示sql语句 -->

<prop key="hibernate.show_sql">

true

</prop>

</props>

</property>

       <!—映射文件的配置-->

<property name="mappingResources">

<list>

<value>entity/Emp.hbm.xml</value>

</list>

</property>

</bean>

两个文件单独配置时,Spring配置如示例4.2所示。

示例4.2

<!-- 加载Hibernate配置 -->

<!-- 定义Hibernate的SessionFactory bean -->

<bean id="sessionFactory"

class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">

<!-- 使用Spring提供的LocalSessionFactoryBean,传入一个Hibernate配置文件的位置 -->

<property name="configLocation" value="file:src/hibernate.cfg.xml">

</property>

</bean>

无论使用哪种整合方式,在Spring配置文件中都有一个sessionFactory,我们知道Hibernate对数据库的操作依靠session完成,而sessionFactory正是用来产生session。

1.1.1 使用HibernateTemplate简化DAO

 

 

Spring提供多种数据库访问技术的DAO支持,可以使用相同的访问模式,使用不同的数据库访问技术。这种DAO支持大大地减少数据库访问的代码量,提高开发效率。接下来我们设计如示例4.3所示的DAO接口。

示例4.3

public interface EmpDao {

public void addEmp(Emp emp);

public void delEmp(short empNo);

public void updateEmp(Emp emp);

public Emp get(short empNo);

public List searchEmp(Emp emp);

public List searchEmp(String hql);

}

我们可以使用Spring提供的HibernateTemplate来简化EmpDao的实现。如示例4.4所示。

示例4.4

public class EmpDaoImpl extends HibernateDaoSupport implements EmpDao {

public void addEmp(Emp emp) {

super.getHibernateTemplate().save(emp);

}

public void delEmp(short empNo) {

super.getHibernateTemplate().delete(this.get(empNo));

}

public Emp get(short empNo) {

Emp emp = (Emp) super.getHibernateTemplate().get(Emp.class, empNo);

return emp;

}

public List searchEmp(final Emp condition) {

List list = super.getHibernateTemplate().executeFind(

new HibernateCallback() {

public Object doInHibernate(Session session)

throws HibernateException, SQLException {

// 构建查询

Criteria c = session.createCriteria(Emp.class);

if (condition != null) {

// 构造Criteria查询条件的代码

}

return c.list();

}

});

return list;

}

public List searchEmp(String hql) {

return super.getHibernateTemplate().find(hql);

}

public void updateEmp(Emp emp) {

super.getHibernateTemplate().update(emp);

}

}

在示例4.4现中,我们发现Spring提供的HibernateDaoSupport类可以简化Hibernate的开发。HibernateDaoSupport类提供了getHibernateTemplate()方法获得HibernateTemplate对象,HibernateTemplate对象提供了非常多的方法,如表4-1-1所示。

4-1-1 HibernateTemplate常用的方法

编号

方法

功能

1

find(String queryString)

根据HQL查询字符串来返回集合示例

2

save(Object entity)

保存新的示例

3

update(Object entity)

根据给定的持久化对象更新记录

4

delete(Object entity)

删除指定持久化实例

5

deleteAll(Collection entities)

删除集合内全部持久化类实例

6

get(Class entityClass,Serializable id)

根据主键加载特定持久化类的实例

7

saveOrUpdate(Object entity)

根据实例状态,选择保存或更新

我们并没有在示例4.4中看到有关SessionFactory的代码,Spring为HibernateDaoSupport类提供了setSessionFactory方法,我们将通过这个setter方法向DAO类注入SessionFactory。配置方式如示例4.5所示。

示例4.5

<!--EmpDaoImpl的配置 -->

<bean id="EmpDao" class="dao.impl.EmpDaoImpl">

<!-- 注入sessionFactory -->

<property name="sessionFactory" ref="sessionFactory"></property>

</bean>

1.1.2 业务层的处理

我们已经完成了EmpDao的定义,将EmpDao注入到empBiz中需要在EmpBiz中增加EmpDao属性及对应setter和getter方法。

代码如示例4.6所示。

示例4.6

public class EmpBizImpl implements EmpBiz {

private EmpDao EmpDao;

public void addEmp(Emp emp) {

EmpDao.addEmp(emp);

}

public void delEmp(short empNo) {

EmpDao.delEmp(empNo);

}

public Emp get(short empNo) {

return EmpDao.get(empNo);

}

public List searchEmp(Emp condition) {

return EmpDao.searchEmp(condition);

}

public List searchAllEmp() {

String hql="from Emp";

return EmpDao.searchEmp(hql);

}

public void updateEmp(Emp emp) {

EmpDao.updateEmp(emp);

}

//setter & getter

}

Spring配置文件中,我们需要在业务逻辑中注入DAO,如示例4.7所示。

示例4.7

<bean id="empBiz" class="biz.impl.EmpBizImpl">

<property name="EmpDao" ref="EmpDao"></property>

</bean>

1.1 声明式事务

之前我们编写的程序,事务都是在DAO层控制,在实际应用开发中显然是不合适的。比如某银行的业务逻辑方法中需要对DAO进行多次调用,如示例4.8所示。

示例4.8

public void  transferAccount(CardVo from,CardVo to,double money)

{

from.setMoney(from.getMoney()+money);

cardDAO.update(from);//修改转账人账户

to.setMoney(to.getMoney()-money);

cardDAO.update(to);//修改收款账户

RecordVO record=new RecordVO(from,to,money);

recordDAO.add(record);//增加交易记录

}

这个业务方法中执行了三个持久化操作,根据业务需求要求这三个操作要么全部成功,要么都不能成功,也就是:客观上需要对业务逻辑层的方法进行事务控制。

事务处理是企业应用开发中不能回避的一个重要问题。在Hibernate风行之前,JDBC是数据持久化的主要手段,为了达到在业务逻辑层进行事务控制,一个通用的做法是将Connection对象以参数的形式传来传去。业务逻辑层应该只出现描述业务逻辑的代码,Connection在业务逻辑层的出现破坏了三层结构的基本原则。另外一个做法是将业务逻辑放到DAO层实现,DAO层依据业务逻辑创建方法,编写复杂的业务逻辑代码,这样进行架构设计的系统很快就会尝到苦果。

事务是系统开发过程中的一个方面,散步在系统里,我们可以通过AOP来实现。

1.1.1 事务通知

使用Spring的声明式事务,我们无需编写程序代码,所有的工作都在配置文件中完成,这种不需要编程的事务称为“声明式事务”。配置事务通知需要先声明一个事务管理器,我们可以新建一个aop.xml文件,将事务配置放在该配置文件中。如示例4.9所示。

示例4.9

<!-- 事务管理器的配置 -->

<bean id="txManager"

class="org.springframework.orm.hibernate3.HibernateTransactionManager">

<property name="sessionFactory">

<!-- 注入applicationContext.xml中配置的sessionFactory -->

<ref bean="sessionFactory" />

</property>

</bean>

接下来可以使用 Spring带有的一个<tx:advice>标签,该标签会创建一个事务通知,我们所需要的就是创建一个切入点,该切入点配置所有带事务的方法并引用事务性通知。事务通知的配置如示例4.10所示,该示例采用Spring2.x方式,需要加入命名空间的引用。

示例4.10

<!-- 事务通知的配置,需要指定一个事务管理器 -->

<tx:advice id="txAdvice" transaction-manager="txManager">

<!-- 定义属性,声明事务规则 -->

<tx:attributes>

<!-- 对get/find/search/query开头的方法要求只读事务 -->

<tx:method name="get*" propagation="SUPPORTS" read-only="true" />

<tx:method name="search*" propagation="SUPPORTS" read-only="true" />

<tx:method name="find*" propagation="SUPPORTS" read-only="true" />

<tx:method name="query*" propagation="SUPPORTS" read-only="true" />

<!-- 对add/del/update/do开头的方法要求必须运行在事务环境中 -->

<tx:method name="del*" propagation="REQUIRED" />

<tx:method name="add*" propagation="REQUIRED" />

<tx:method name="update*" propagation="REQUIRED" />

<tx:method name="do*" propagation="REQUIRED" />

<!-- 对其他方法采用如下配置 -->

<tx:method name="*" propagation="REQUIRED" read-only="true" />

</tx:attributes>

</tx:advice>

<tx:advice>标签内设值id和transaction-manager属性,id是advice Bean的标识,而transaction-manager引用一个事务管理器。

除了这两个属性外,还可以通过<tx:attributes/>标签配置事务属性。<tx:method>的propagation属性表示事务的传播行为,REQUIRED表示如果存在一个事务,则支持当前事务,如果当前没有事务,则开启一个新的事务。SUPPORTS表示如果存在一个事务,则支持当前事务,如果当前没有事务,则按非事务方式执行。<tx:method> 的属性name="*"表示匹配所有其他的方法readOnly表示启用只读事务。这样数据库库就可以采取合适的优化措施避免不必要的操作。

1.1.2 事务通知和切入点组合

我们已经定义一个id为txAdvice的事务通知,接下来,我们需要将事务通知和切入点组合,最合适的切入点是biz的方法,可以是接口中声明的方法,也可以直接将切入点定义为biz实现的方法。代码片段如示例4.11所示。

示例4.11

<!-- 将事务通知和切入点组合 -->

<aop:config>

<aop:pointcut expression="execution(* biz.*.*(..))" id="pc" />

<aop:advisor advice-ref="txAdvice" pointcut-ref="pc" />

</aop:config>

上面的代码表示biz包下所有类的所有方法都应用事务规则。至此,Spring与 Hibernate的整合完成。数据库操作还是需要Hibernate完成,Spring起到管理的作用,比如Spring管理各个Bean,管理Hibernate的事务。这样的代码不但简介,而且健壮性也得到了很好的保证,是企业应用开发的首选。

对以上整合的测试代码如示例4.12所示。

示例4.12

public static void main(String[] args) throws ParseException {

ApplicationContext context = new ClassPathXmlApplicationContext(

new String[] { "applicationContext.xml", "aop.xml" });

EmpBiz empBiz = (EmpBiz) context.getBean("empBiz");

Emp emp = new Emp();

emp.setEmpno((short) 1000);

emp.setEname("张翠山");

emp.setJob("主管");

emp.setMgr((short) 7879);

SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");

Date hiredate = sdf.parse("2001-12-12");

emp.setHiredate(new Timestamp(hiredate.getTime()));

emp.setSal(8000.0);

emp.setComm(2000.0);

emp.setDeptno((byte) 20);

empBiz.addEmp(emp);//添加新员工

}

添加之后,数据库的记录如图4.1.11所示:




测试查询所有的员工,如示例4.13所示。

示例4.13

List<Emp> empList = empBiz.searchAllEmp();

for (Emp emp : empList) {

System.out.println(emp.getEmpno() + "\t" + emp.getEname() + "\t"

+ emp.getSal() + "\t" + emp.getComm());

}

测试结果如图4.1.12所示。


1.1 使用工具生成Spring DAO

开发过程中,一般通过反向工程生成表对应的实体对象,同时也可以勾选Spring DAO选项,通过MyEclipse生成实体对象的DAO。操作如图4.1.13所示。




生成的DAO代码如示例4.14所示。

示例4.14

public class EmpDao extends HibernateDaoSupport {

private static final Log log = LogFactory.getLog(EmpDao.class);

// property constants

public static final String ENAME = "ename";

public static final String JOB = "job";

public static final String MGR = "mgr";

public static final String SAL = "sal";

public static final String COMM = "comm";

public static final String DEPTNO = "deptno";

protected void initDao() {

// do nothing

}

public void save(Emp transientInstance) {

log.debug("saving Emp instance");

try {

getHibernateTemplate().save(transientInstance);

log.debug("save successful");

} catch (RuntimeException re) {

log.error("save failed", re);

throw re;

}

}

public void delete(Emp persistentInstance) {

log.debug("deleting Emp instance");

try {

getHibernateTemplate().delete(persistentInstance);

log.debug("delete successful");

} catch (RuntimeException re) {

log.error("delete failed", re);

throw re;

}

}

public Emp findById(java.lang.Short id) {

log.debug("getting Emp instance with id: " + id);

try {

Emp instance = (Emp) getHibernateTemplate().get("orm.Emp", id);

return instance;

} catch (RuntimeException re) {

log.error("get failed", re);

throw re;

}

}

public List findByExample(Emp instance) {

log.debug("finding Emp instance by example");

try {

List results = getHibernateTemplate().findByExample(instance);

log.debug("find by example successful, result size: "

+ results.size());

return results;

} catch (RuntimeException re) {

log.error("find by example failed", re);

throw re;

}

}

public List findByProperty(String propertyName, Object value) {

log.debug("finding Emp instance with property: " + propertyName

+ ", value: " + value);

try {

String queryString = "from Emp as model where model."

+ propertyName + "= ?";

return getHibernateTemplate().find(queryString, value);

} catch (RuntimeException re) {

log.error("find by property name failed", re);

throw re;

}

}

public List findByEname(Object ename) {

return findByProperty(ENAME, ename);

}

public List findByJob(Object job) {

return findByProperty(JOB, job);

}

public List findByMgr(Object mgr) {

return findByProperty(MGR, mgr);

}

public List findBySal(Object sal) {

return findByProperty(SAL, sal);

}

public List findByComm(Object comm) {

return findByProperty(COMM, comm);

}

public List findByDeptno(Object deptno) {

return findByProperty(DEPTNO, deptno);

}

public List findAll() {

log.debug("finding all Emp instances");

try {

String queryString = "from Emp";

return getHibernateTemplate().find(queryString);

} catch (RuntimeException re) {

log.error("find all failed", re);

throw re;

}

}

public Emp merge(Emp detachedInstance) {

log.debug("merging Emp instance");

try {

Emp result = (Emp) getHibernateTemplate().merge(detachedInstance);

log.debug("merge successful");

return result;

} catch (RuntimeException re) {

log.error("merge failed", re);

throw re;

}

}

public void attachDirty(Emp instance) {

log.debug("attaching dirty Emp instance");

try {

getHibernateTemplate().saveOrUpdate(instance);

log.debug("attach successful");

} catch (RuntimeException re) {

log.error("attach failed", re);

throw re;

}

}

public void attachClean(Emp instance) {

log.debug("attaching clean Emp instance");

try {

getHibernateTemplate().lock(instance, LockMode.NONE);

log.debug("attach successful");

} catch (RuntimeException re) {

log.error("attach failed", re);

throw re;

}

}

public static EmpDao getFromApplicationContext(ApplicationContext ctx) {

return (EmpDao) ctx.getBean("EmpDao");

}

}

以上代码我们可以看到,使用工具生成的DAO中有大量的方法,这些方法我们可以直接在业务层调用,也会在配置文件中自动配置。这些方法的功能描述见表4-1-2所示。

4-1-2 DAO的方法及功能描述

方法名称

描述

void save(Emp transientInstance)

根据对象添加

delete(Emp persistentInstance)

根据对象删除

findById(java.lang.Short id)

根据ID查询对象

findByExample(Emp instance)

根据对象查询,查询条件为对象的属性值

findByEname(Object ename)

根据员工名查询

findAll()

查询所有员工

merge(Emp detachedInstance)

合并session中存在相同ID的对象

attachDirty(Emp instance)

添加与修改,相当于saveOrUpdate

attachClean(Emp instance)

将传入的对象状态设值为Transient状态


本章总结

 

Ø Spring整合Hibernate

配置SessionFactory

n HibernateTemplate

n HibernateDaoSupport

Ø 声明式事务管理

n 配置事物管理器HibernateTransactionManager

使用<tx:advice>结合事务管理器配置事物通知

<tx:advice>内使用<tx:attributes>和<tx:method>针对不同的方法配置事物属性

Ø 使用工具自动生成 Spring DAO

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

任务实训部分

1:完成银行账户系统的框架搭建

训练技能点

Ø sessionFactory的配置

Ø HibernateTemplate的使用

需求说明

某银行系统需要开发一个小系统,该系统能查看账户信息、销户、开户、转账、查看交易记录等功能,要求使用Spring+Hibernate实现。该系统两张表,分别是账户信息表、交易记录表,账户信息表的字段说明如表4-2-1所示。

4-2-1 账户信息表(ACCOUNT)

编号

字段名称

字段说明

1

ACCOUNT_ID

账户ID,主键使用序列seq_account,number(10)

2

USER_NAME

账户名,varchar2(20);

3

MONEY

账户余额,number(6)

4

CREATEDATE

开户时间,date

交易记录表的字段说明如表4-2-2所示。

4-2-2 交易记录表(TBL_RECORD)

编号

字段名称

字段说明

1

RECORD_ID

交易编号,主键使用序列seq_record,number(10)

2

FROMACCOUNT

转出帐号,number(10)

3

TOACCOUNT

转入帐号,number(10)

4

RECORD_DATE

交易时间,date

5

REMARK

备注 varchar2(80)

 

 

实现思路

(1) 创建数据表。

(2) 为项目添加Spring和Hibernate支持。

(3) 编写实体类和映射文件。

(4) 定义DAO接口和实现。

(5) 定义Biz接口和实现。

关键代码

(1) 配置文件的配置。

 <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">

<property name="driverClassName" value="oracle.jdbc.OracleDriver">

</property>

<property name="url" value="jdbc:oracle:thin:@127.0.0.1:1521:orcl">

</property>

<property name="username" value="scott"></property>

<property name="password" value="tiger"></property>

</bean>

<bean id="sessionFactory"

class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">

<property name="dataSource">

<ref bean="dataSource" />

</property>

<property name="hibernateProperties">

<props>

<prop key="hibernate.dialect">

org.hibernate.dialect.Oracle9Dialect

</prop>

<prop key="hibernate.show_sql">

true

</prop>

</props>

</property>

<property name="mappingResources">

<list>

<value>orm/Account.hbm.xml</value>

<value>orm/TblRecord.hbm.xml</value>

</list>

</property>

</bean>

(2)  实体类和映射文件。

//账户实体类

 public class Account implements java.io.Serializable {

private Long accountId;//账户编号

private String userName;//账户名

private Integer money;//余额

private Timestamp createdate;//开户时间

//setter & getter

}

//交易记录实体

public class TblRecord implements java.io.Serializable {

private Long recordId;//交易编号

private Long fromaccount;//转账账户编号

private Long toaccount;//收款帐号

private Timestamp recordDate;//转账时间

private String remark;//备注

//setter & getter

}

(3) 定义DAO接口和实现,DAO实现需要继承HibernateDaoSupport,但不写方法和方法体。

//账户DAO

public interface AccountDAO {

}

//交易记录DAO

public interface RecordDAO {

}

(4) 定义Biz接口和实现,Biz的实现类需要增加两个属性,AccountDAO和RecordDAO,并生成setter和getter方法,但不添加任何方法和方法体。

//业务逻辑接口

public interface BankBiz {

}

2:声明式事务配置

训练技能点

Ø 声明式事务

需求说明

完善框架搭建,增加Biz和DAO的配置件和声明式事务。

实现思路

(1) 增加Biz和DAO的配置。

(2) 编写配置文件,增加声明式事务配置。

3:实现银行系统的开户和查看账户信息功能

训练技能点

Ø HibernateTemplate的使用

需求说明

实现开户和账户信息查询功能。

实现思路

(1) 完善DAO层的实现,编写方法和方法体。

(2) 完善Biz层的实现,编写方法和方法体。

(3) 编写测试代码,测试开户和账户信息查看功能。

关键代码

(1)在DAO中增加以下方法声明,并实现DAO的方法。

//RecordDAO中的方法声明

public TblRecord get(long id);

public void addRecord(TblRecord record);//增加交易记录

public void delRecord(long recordId);//删除交易记录

//AccountDAO中的方法声明

public Account get(long id);//根据主键查询账户信息

public void addAccount(Account account);//添加账户信息

public void delAccount(long id);//根据主键删除账户信息

public void updateAccount(Account account);//修改账户信息

public List findAllAccount();//查询所有账户信息

(2) 在Biz中增加以下方法声明,并实现Biz的方法。

//BankBiz接口中添加的方法声明

public Account findAccountById(long id);//查询账户信息

public void doAddAcount(Account account);//用户开户

//BankBiz方法的实现

public void doAddAcount(Account account) {

accountDAO.addAccount(account);

}

public Account findAccountById(long id) {

return accountDAO.get(id);

}

4:完善银行系统,增加转账功能

训练技能点

Ø HibernateTemplate的使用

Ø 业务逻辑层的实现

需求说明

实现转账功能。

实现思路

(1) 在Biz接口中增加转账方法声明。

(2) 实现转账方法。

(3) 编写测试代码,测试转账功能。

关键代码

(1)在Biz接口中增加转账方法声明。

//BankBiz中声明转账方法

public boolean doTransferAccount(long from,long to,int money);//转账

 (2) 实现转账方法。

public boolean doTransferAccount(long from, long to, int money) {

boolean result = false;

// 根据编号获取账户信息

Account from_Account = accountDAO.get(from);

Account to_Account = accountDAO.get(to);

if (from_Account!=null&&from_Account.getMoney() > money)// 如果余额大于转账金额

{

if (to_Account != null)// 如果收款帐号存在

{

   //扣除转账人账户余额

//添加收款人账户余额

//记录交易记录

result = true;

}

}

return result;

}

 


巩固练习

 

一.选择题

    以下关于Spring对Hibernate提供支持的说法中,错误的是()。

    A. Spring提供了基类大大简化了程序代码

    B. Spring提供的基类支持分页查询

    C. Spring提供的基类需要注入sessionFactory才能正常运行

    D. Spring提供的基类不需要注入sessionFactory

2. Spring整合Hibernate后,能够用于保存数据的方法有()。

    A. save

    B. load

    C. saveOrUpdate

    D. merge

3. Spring整合Hibernate后,以下关于findByCriteria方法的说法中,正确的是()。

    A. 参数是HQL

    B. 参数是Restrictions对象

    C. 参数是DetachedCriteria对象

    D. 该方法不支持分页

4. 以下关于 Spring与Hibernate集成的说法中,错误的是()。

    A. 通过集成Spring与Hibernate,以Spring管理程序的依赖关系,将SessionFactory注入 DataSource

    B. 通过Spring,在Biz层代码中无须直接实例化DAO类,而是通过注入获得

    C. 通过Spring,在DAO类中无须实例化SessionFactory,而是通过注入获得

D. Spring提供HibemateDaoSupport类简化Hibernate的使用

5.关于声明式事务,下面说法错误的是()。

A.Spring采用AOP方式实现声明式事务

B.声明式事务是非侵入式的,可以不修改原来代码就给系统增加事务支持

C.配置声明式事务必须tx和aop两个命名空间的支持

D.事务有编程式事务和非编程式事务,声明式事务是非编程式事务

二.操作题

1.结合EMP的CURD,编写一个业务方法,实现员工的分页查询,每页显示5条员工信息。

2.查看课外书籍,使用注解重构实训任务的声明式事务。

3.升级实训任务,实现交易记录的分页查询。

提示:分页查询的实现

List list = super.getHibernateTemplate().executeFind(

new HibernateCallback() {

public Object doInHibernate(Session session)

throws HibernateException, SQLException {

String hql = "from TblRecord record where  record.fromaccount=" + fromId;

Query query = session.createQuery(hql);

query.setFirstResult(firstResult);

query.setMaxResults(5);

return query.list();

}

});

4.重构“会员账户管理系统”,使用Hibernate实现DAO层开发,并编写测试代码,测试业务逻辑层的功能。要求使用声明式事务,每项业务操作都必须有日志记录。



作者:zhangchen124 发表于2017/11/10 21:09:26 原文链接
阅读:21 评论:0 查看评论

Spring实现原理分析(二十四).Spring Boot实现原理分析

$
0
0

       前阵子在分析sprng boot的源代码,有了些感悟和心得,今天写篇博客和大家分享下。先来段题外话,在人体的血液中含有血细胞,而血细胞又大致可以分为红细胞、白细胞、血小板。它们各自有各自的用处和特点,互相协作保障人体的建康。


一. 各种Bean   

        如果我们把Spring想象成人体,把Bean当做细胞的话,那么正是不同种类bean的相互协作才使得spring这个大工厂正常运行,有些bean做管理工作,有些bean为其它bean服务器,有些bean生产其它bean,有些bean承载了应用的业务逻辑。根据我目前的认知,我觉得spring中的bean根据作用可以划分为“普通bean”、“容器后处理器”、“bean后处理器”、“工厂bean”、“工厂方法bean” 这五大类。

    

      


普通bean:我这里指的是普通的用户配置的实现应用业务逻辑的bean,比如Dao Bean 或 Service Bean。

容器后处理器bean:这类bean它是为Spring容器服务的,实现了容器后处理器接口。比如像PropertySourcesPlaceholderConfigurer,它的作用就是在创建bean定义对象之前,解析用户配置的非线性值(${..})。

bean后处理器:这类bean它是为其它bean服务的,实现了bean后处理接口。这类bean可以在创建其它bean过程中的各个环节执行干预操作,比如ApplicationContextAwareProcessor,它会在初始化bean的阶段为bean的属性赋值敏感对象。

工厂bean:这类bean它实现了FactoryBean接口,即返回的bean对象不是bean本身,而是接口getObject方法的返回对象,如果希望获取工厂bean自身,引用是bean名称前面加&符号。比如MethodInvokingFactoryBean就是Spring实现的工厂bean,用于调用指定对象的方法。

工厂方法bean:又可以细分为静态工厂方法bean和实例工厂方法bean。这类bean通过调用指定的工厂方法返回bean对象。不要小看这类bean,配置类中被@bean注解的方法对象会被解析成bean定义对象,其类型就是工厂方法bean。

  我写上面这段内容的目的是希望读者明白,Spring能实现Boot,本质上是因为有这些不同功能的Bean做支撑。换句话说,Spring Boot并不是对原有Spring框架的颠覆,而是基于原有功能的一次进化,从而大大减少了不必要的配置工作量,使得整个框架变得更干净、更轻便。


二. Spring Boo初始化过程.    

       我们知道spring boot最为人津津乐道的是大量减轻了配置工作量,只要有一个被@SpringBootApplication注解的启动类就可以运行。就像生命的孕育过程,从一个小小的细胞变成五脏俱全的新生儿,启动类之所以是启动类,本质是因为它被@SpringBootApplication注解了,因此@SpringBootApplication就是那个蕴含了强大生命力的细胞。那么,对@SpringBootApplication注解的解析是发生的什么时候,由谁完成的呢?答案是发生在spring启动过程中对容器的初始化阶段,是由ConfigurationClassPostProcessor(容器后处理器)完成的

      spring boot所以能大量减少用户的配置工作量,特别是减少了配置集成第三方组件的工作量,如mybatis,hibernate,redis。是因为spring默认编写了很多配置类(被@Configuration注解的类),这些类位于autoconfig's jar里面。实现Boot的核心逻辑就是导入这些配置类,然后把它们转换成Bean定义对象(可以把这些配置类想象成食物,被摄入后转换成糖和碳水化合物)。 我现在还无法清楚准确的了解每个配置类的具体功能,但是读者要确信一点,配置类就是Boot的灵魂。比如有个配置类,

@Configuration
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
public class PropertyPlaceholderAutoConfiguration 
{
	@Bean
	@ConditionalOnMissingBean(search = SearchStrategy.CURRENT)
	public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer()
	{
		return new PropertySourcesPlaceholderConfigurer();
	}
}
这个配置类被导入后,会转换成两个Bean定义对象,配置自身是一个bean定义对象(姑且叫beanA),被@Bean注解的propertySourcesPlaceholderConfigurer方法是一个bean定义对象(姑且叫beanB)。然后beanB是一个静态工厂方法bean,工厂方法是propertySourcesPlaceholderConfigurer,被生产的对象是PropertySourcesPlaceholderConfigurer 。PropertySourcesPlaceholderConfigurer本身是一个容器后处理器,它的其中一个作用是解析bean定义对象非线性属性值。

     

下面我描述下导入配置类和提取bean定义对象过程:

A. 进入ConfigurationClassPostProcessor(容器后处理器)的方法。

B. 从bean工厂获取配置类bean定义对象(通常此时唯一的配置类bean定义对象就是启动类)。

C. 解析配置类bean定义对象,从而获取spring系统配置类。这里需要介绍几个重要的注解对象,首先是@enableautoconfiguration,@SpringBootApplication被@enableautoconfiguration注解了,而@enableautoconfiguration又被@import注解了,@import导入了EnableAutoConfigurationImportSelector(导入选择器),它会读取在autoconfig's jar包里的spring.factories文件, 其中key是"EnableAutoConfiguration"的类(都是配置类)。至于是否导入,会读取spring-autoconfigure-metadata.properties文件内容,根据@conditionalonclass注解判断配置类依赖的jar包是否存在。

D. 创建ConfigurationClassBeanDefinitionReader(配置类bean定义阅读器),从步骤C获取的配置类中提取bean定义对象,注册到bean工厂。阅读器会从以下四个方面提取bean定义对象.

     d.1. 配置类自身,如果它被其它配置类导入了。

     d.2. 配置类中被@Bean注解的方法。

     d.3.配置类@ImportResource导入的xml配置文件。

     d.4.配置类@Import导入的ImportBeanDefinitionRegistrar。




作者:roberts939299 发表于2017/11/10 23:00:28 原文链接
阅读:23 评论:0 查看评论

深度探索C++ 对象模型【第五章3】

$
0
0

1:总结一下拷贝赋值运算符

  • 较为复杂,不做细节讨论
  • 其在虚拟继承下行为不佳,需要加以小心的设计和使用说明
  • 建议尽可能不要允许虚基类函数的拷贝操作
  • 建议不要在虚基类函数中声明数据
2:Explict Initialization List 要注意和成员初始化列表相区别开来。
Point A = {1,2,3};//显示初始化列表
//与构造函数中的成员初始化列表有明显的区别


3:总结一点:继承体系的增加,和继承复杂度的增加以及虚拟继承的引入,都会使得对象的初始化和拷贝操作的时间成本有一定的增加。

4:如果class没有定义一个析构函数,那么只有在类内含有member object 或者是class 的基类有自己的析构函数的状态下,编译器才会自动合成出来一个析构函数。否则,析构函数不会被合成出来。(注意与构造函数与拷贝构造函数之间的区别,这次只有两种情况,也就是说,虚函数的存在,并不需要析构函数的参与

5:即使是定义了一个构造函数,也不应当理所当然的去定义或者合成一个析构函数,真正影响到它们存在的是是否需要该函数。

6:一个由程序员所定义的析构函数的扩展内容
  • 析构函数的本体首先被执行
  • 如果class有member object ,而该成员对象有析构函数,那么它们会以声明顺序相反的顺序被调用
  • 如果对象中含有vptr,现在会被重新设定,指向适当的基类的虚函数表
  • 如果有任何直接的(上一层)非虚基类有析构函数,它们会以声明顺序相反的顺序被调用
  • 如果有虚基类函数,且其有析构函数,如若讨论类为,最末端的派生类(most_derived class) ,则这些析构函数会以原来的构造顺序的相反顺序被调用
7:一个类对象的声明结束于析构函数开始执行时,由于每一个基类的析构函数轮番被调用,所以派生类实际上变成了一个完整的对象,随着基类析构函数的调用,类对象的大小逐渐减小,直到最后一个基类的析构函数调用(vptr的变化也随之改变)。



           

作者:misayaaaaa 发表于2017/11/11 9:02:12 原文链接
阅读:0 评论:0 查看评论

吴恩达Coursera深度学习课程 DeepLearning.ai 编程作业——Optimization Methods(2-2)

$
0
0

Optimization Methods

Until now, you’ve always used Gradient Descent to update the parameters and minimize the cost. In this notebook, you will learn more advanced optimization methods that can speed up learning and perhaps even get you to a better final value for the cost function. Having a good optimization algorithm can be the difference between waiting days vs. just a few hours to get a good result.

Gradient descent goes “downhill” on a cost function J. Think of it as trying to do this:

这里写图片描述

At each step of the training, you update your parameters following a certain direction to try to get to the lowest possible point.

Notations: As usual, Ja= da for any variable a.

To get started, run the following code to import the libraries you will need.

import numpy as np
import matplotlib.pyplot as plt
import scipy.io
import math
import sklearn
import sklearn.datasets

from opt_utils import load_params_and_grads, initialize_parameters, forward_propagation, backward_propagation
from opt_utils import compute_cost, predict, predict_dec, plot_decision_boundary, load_dataset
from testCases import *

plt.rcParams['figure.figsize'] = (7.0, 4.0) # set default size of plots
plt.rcParams['image.interpolation'] = 'nearest'
plt.rcParams['image.cmap'] = 'gray'

opt_utils.py

import numpy as np
import matplotlib.pyplot as plt
import h5py
import scipy.io
import sklearn
import sklearn.datasets

def sigmoid(x):
    """
    Compute the sigmoid of x

    Arguments:
    x -- A scalar or numpy array of any size.

    Return:
    s -- sigmoid(x)
    """
    s = 1/(1+np.exp(-x))
    return s

def relu(x):
    """
    Compute the relu of x

    Arguments:
    x -- A scalar or numpy array of any size.

    Return:
    s -- relu(x)
    """
    s = np.maximum(0,x)

    return s

def load_params_and_grads(seed=1):
    np.random.seed(seed)
    W1 = np.random.randn(2,3)
    b1 = np.random.randn(2,1)
    W2 = np.random.randn(3,3)
    b2 = np.random.randn(3,1)

    dW1 = np.random.randn(2,3)
    db1 = np.random.randn(2,1)
    dW2 = np.random.randn(3,3)
    db2 = np.random.randn(3,1)

    return W1, b1, W2, b2, dW1, db1, dW2, db2


def initialize_parameters(layer_dims):
    """
    Arguments:
    layer_dims -- python array (list) containing the dimensions of each layer in our network

    Returns:
    parameters -- python dictionary containing your parameters "W1", "b1", ..., "WL", "bL":
                    W1 -- weight matrix of shape (layer_dims[l], layer_dims[l-1])
                    b1 -- bias vector of shape (layer_dims[l], 1)
                    Wl -- weight matrix of shape (layer_dims[l-1], layer_dims[l])
                    bl -- bias vector of shape (1, layer_dims[l])

    Tips:
    - For example: the layer_dims for the "Planar Data classification model" would have been [2,2,1]. 
    This means W1's shape was (2,2), b1 was (1,2), W2 was (2,1) and b2 was (1,1). Now you have to generalize it!
    - In the for loop, use parameters['W' + str(l)] to access Wl, where l is the iterative integer.
    """

    np.random.seed(3)
    parameters = {}
    L = len(layer_dims) # number of layers in the network

    for l in range(1, L):
        parameters['W' + str(l)] = np.random.randn(layer_dims[l], layer_dims[l-1])*  np.sqrt(2 / layer_dims[l-1])
        parameters['b' + str(l)] = np.zeros((layer_dims[l], 1))

        assert(parameters['W' + str(l)].shape == layer_dims[l], layer_dims[l-1])
        assert(parameters['W' + str(l)].shape == layer_dims[l], 1)

    return parameters


def compute_cost(a3, Y):

    """
    Implement the cost function

    Arguments:
    a3 -- post-activation, output of forward propagation
    Y -- "true" labels vector, same shape as a3

    Returns:
    cost - value of the cost function
    """
    m = Y.shape[1]

    logprobs = np.multiply(-np.log(a3),Y) + np.multiply(-np.log(1 - a3), 1 - Y)
    cost = 1./m * np.sum(logprobs)

    return cost

def forward_propagation(X, parameters):
    """
    Implements the forward propagation (and computes the loss) presented in Figure 2.

    Arguments:
    X -- input dataset, of shape (input size, number of examples)
    parameters -- python dictionary containing your parameters "W1", "b1", "W2", "b2", "W3", "b3":
                    W1 -- weight matrix of shape ()
                    b1 -- bias vector of shape ()
                    W2 -- weight matrix of shape ()
                    b2 -- bias vector of shape ()
                    W3 -- weight matrix of shape ()
                    b3 -- bias vector of shape ()

    Returns:
    loss -- the loss function (vanilla logistic loss)
    """

    # retrieve parameters
    W1 = parameters["W1"]
    b1 = parameters["b1"]
    W2 = parameters["W2"]
    b2 = parameters["b2"]
    W3 = parameters["W3"]
    b3 = parameters["b3"]

    # LINEAR -> RELU -> LINEAR -> RELU -> LINEAR -> SIGMOID
    z1 = np.dot(W1, X) + b1
    a1 = relu(z1)
    z2 = np.dot(W2, a1) + b2
    a2 = relu(z2)
    z3 = np.dot(W3, a2) + b3
    a3 = sigmoid(z3)

    cache = (z1, a1, W1, b1, z2, a2, W2, b2, z3, a3, W3, b3)

    return a3, cache

def backward_propagation(X, Y, cache):
    """
    Implement the backward propagation presented in figure 2.

    Arguments:
    X -- input dataset, of shape (input size, number of examples)
    Y -- true "label" vector (containing 0 if cat, 1 if non-cat)
    cache -- cache output from forward_propagation()

    Returns:
    gradients -- A dictionary with the gradients with respect to each parameter, activation and pre-activation variables
    """
    m = X.shape[1]
    (z1, a1, W1, b1, z2, a2, W2, b2, z3, a3, W3, b3) = cache

    dz3 = 1./m * (a3 - Y)
    dW3 = np.dot(dz3, a2.T)
    db3 = np.sum(dz3, axis=1, keepdims = True)

    da2 = np.dot(W3.T, dz3)
    dz2 = np.multiply(da2, np.int64(a2 > 0))
    dW2 = np.dot(dz2, a1.T)
    db2 = np.sum(dz2, axis=1, keepdims = True)

    da1 = np.dot(W2.T, dz2)
    dz1 = np.multiply(da1, np.int64(a1 > 0))
    dW1 = np.dot(dz1, X.T)
    db1 = np.sum(dz1, axis=1, keepdims = True)

    gradients = {"dz3": dz3, "dW3": dW3, "db3": db3,
                 "da2": da2, "dz2": dz2, "dW2": dW2, "db2": db2,
                 "da1": da1, "dz1": dz1, "dW1": dW1, "db1": db1}

    return gradients

def predict(X, y, parameters):
    """
    This function is used to predict the results of a  n-layer neural network.

    Arguments:
    X -- data set of examples you would like to label
    parameters -- parameters of the trained model

    Returns:
    p -- predictions for the given dataset X
    """

    m = X.shape[1]
    p = np.zeros((1,m), dtype = np.int)

    # Forward propagation
    a3, caches = forward_propagation(X, parameters)

    # convert probas to 0/1 predictions
    for i in range(0, a3.shape[1]):
        if a3[0,i] > 0.5:
            p[0,i] = 1
        else:
            p[0,i] = 0

    # print results

    #print ("predictions: " + str(p[0,:]))
    #print ("true labels: " + str(y[0,:]))
    print("Accuracy: "  + str(np.mean((p[0,:] == y[0,:]))))

    return p

def load_2D_dataset():
    data = scipy.io.loadmat('datasets/data.mat')
    train_X = data['X'].T
    train_Y = data['y'].T
    test_X = data['Xval'].T
    test_Y = data['yval'].T

    plt.scatter(train_X[0, :], train_X[1, :], c=train_Y, s=40, cmap=plt.cm.Spectral);

    return train_X, train_Y, test_X, test_Y

def plot_decision_boundary(model, X, y):
    # Set min and max values and give it some padding
    x_min, x_max = X[0, :].min() - 1, X[0, :].max() + 1
    y_min, y_max = X[1, :].min() - 1, X[1, :].max() + 1
    h = 0.01
    # Generate a grid of points with distance h between them
    xx, yy = np.meshgrid(np.arange(x_min, x_max, h), np.arange(y_min, y_max, h))
    # Predict the function value for the whole grid
    Z = model(np.c_[xx.ravel(), yy.ravel()])
    Z = Z.reshape(xx.shape)
    # Plot the contour and training examples
    plt.contourf(xx, yy, Z, cmap=plt.cm.Spectral)
    plt.ylabel('x2')
    plt.xlabel('x1')
    plt.scatter(X[0, :], X[1, :], c=y, cmap=plt.cm.Spectral)
    plt.show()

def predict_dec(parameters, X):
    """
    Used for plotting decision boundary.

    Arguments:
    parameters -- python dictionary containing your parameters 
    X -- input data of size (m, K)

    Returns
    predictions -- vector of predictions of our model (red: 0 / blue: 1)
    """

    # Predict using forward propagation and a classification threshold of 0.5
    a3, cache = forward_propagation(X, parameters)
    predictions = (a3 > 0.5)
    return predictions

def load_dataset():
    np.random.seed(3)
    train_X, train_Y = sklearn.datasets.make_moons(n_samples=300, noise=.2) #300 #0.2 
    # Visualize the data
    plt.scatter(train_X[:, 0], train_X[:, 1], c=train_Y, s=40, cmap=plt.cm.Spectral);
    train_X = train_X.T
    train_Y = train_Y.reshape((1, train_Y.shape[0]))

    return train_X, train_Y

1 - Gradient Descent

A simple optimization method in machine learning is gradient descent (GD). When you take gradient steps with respect to all m examples on each step, it is also called Batch Gradient Descent.

Warm-up exercise: Implement the gradient descent update rule. The gradient descent rule is, for l=1,...,L:

W[l]=W[l]α dW[l](1)

b[l]=b[l]α db[l](2)

where L is the number of layers and α is the learning rate. All parameters should be stored in the parameters dictionary. Note that the iterator l starts at 0 in the for loop while the first parameters are W[1] and b[1]. You need to shift l to l+1 when coding.

def update_parameters_with_gd(parameters, grads, learning_rate):
    """
    Update parameters using one step of gradient descent

    Arguments:
    parameters -- python dictionary containing your parameters to be updated:
                    parameters['W' + str(l)] = Wl
                    parameters['b' + str(l)] = bl
    grads -- python dictionary containing your gradients to update each parameters:
                    grads['dW' + str(l)] = dWl
                    grads['db' + str(l)] = dbl
    learning_rate -- the learning rate, scalar.

    Returns:
    parameters -- python dictionary containing your updated parameters 
    """

    L = len(parameters) // 2 # number of layers in the neural networks

    # Update rule for each parameter
    for l in range(L):
        ### START CODE HERE ### (approx. 2 lines)
        parameters["W" + str(l+1)] = parameters["W"+str(l+1)]-learning_rate*grads["dW"+str(l+1)]
        parameters["b" + str(l+1)] = parameters["b"+str(l+1)]-learning_rate*grads["db"+str(l+1)]
        ### END CODE HERE ###

    return parameters

A variant of this is Stochastic Gradient Descent (SGD), which is equivalent to mini-batch gradient descent where each mini-batch has just 1 example. The update rule that you have just implemented does not change. What changes is that you would be computing gradients on just one training example at a time, rather than on the whole training set. The code examples below illustrate the difference between stochastic gradient descent and (batch) gradient descent.

  • (Batch) Gradient Descent:
X = data_input
Y = labels
parameters = initialize_parameters(layers_dims)
for i in range(0, num_iterations):
    # Forward propagation
    a, caches = forward_propagation(X, parameters)
    # Compute cost.
    cost = compute_cost(a, Y)
    # Backward propagation.
    grads = backward_propagation(a, caches, parameters)
    # Update parameters.
    parameters = update_parameters(parameters, grads)
  • Stochastic Gradient Descent:
X = data_input
Y = labels
parameters = initialize_parameters(layers_dims)
for i in range(0, num_iterations):
    for j in range(0, m):
        # Forward propagation
        a, caches = forward_propagation(X[:,j], parameters)
        # Compute cost
        cost = compute_cost(a, Y[:,j])
        # Backward propagation
        grads = backward_propagation(a, caches, parameters)
        # Update parameters.
        parameters = update_parameters(parameters, grads)

In Stochastic Gradient Descent, you use only 1 training example before updating the gradients. When the training set is large, SGD can be faster. But the parameters will “oscillate” toward the minimum rather than converge smoothly. Here is an illustration of this:

这里写图片描述

Note also that implementing SGD requires 3 for-loops in total:
1. Over the number of iterations
2. Over the m training examples
3. Over the layers (to update all parameters, from (W[1],b[1]) to (W[L],b[L]))

In practice, you’ll often get faster results if you do not use neither the whole training set, nor only one training example, to perform each update. Mini-batch gradient descent uses an intermediate number of examples for each step. With mini-batch gradient descent, you loop over the mini-batches instead of looping over individual training examples.

这里写图片描述

What you should remember:
- The difference between gradient descent, mini-batch gradient descent and stochastic gradient descent is the number of examples you use to perform one update step.
- You have to tune a learning rate hyperparameter α.
- With a well-turned mini-batch size, usually it outperforms either gradient descent or stochastic gradient descent (particularly when the training set is large).

2 - Mini-Batch Gradient descent

Let’s learn how to build mini-batches from the training set (X, Y).

There are two steps:
- Shuffle: Create a shuffled version of the training set (X, Y) as shown below. Each column of X and Y represents a training example. Note that the random shuffling is done synchronously between X and Y. Such that after the shuffling the ith column of X is the example corresponding to the ith label in Y. The shuffling step ensures that examples will be split randomly into different mini-batches.

  • Partition: Partition the shuffled (X, Y) into mini-batches of size mini_batch_size (here 64). Note that the number of training examples is not always divisible by mini_batch_size. The last mini batch might be smaller, but you don’t need to worry about this. When the final mini-batch is smaller than the full mini_batch_size, it will look like this:

这里写图片描述

Exercise: Implement random_mini_batches. We coded the shuffling part for you. To help you with the partitioning step, we give you the following code that selects the indexes for the 1st and 2nd mini-batches:

first_mini_batch_X = shuffled_X[:, 0 : mini_batch_size]
second_mini_batch_X = shuffled_X[:, mini_batch_size : 2 * mini_batch_size]
...

Note that the last mini-batch might end up smaller than mini_batch_size=64. Let s represents s rounded down to the nearest integer (this is math.floor(s) in Python). If the total number of examples is not a multiple of mini_batch_size=64 then there will be mmini_batch_size mini-batches with a full 64 examples, and the number of examples in the final mini-batch will be (mmini_batch_size×mmini_batch_size).

def random_mini_batches(X, Y, mini_batch_size = 64, seed = 0):
    """
    Creates a list of random minibatches from (X, Y)

    Arguments:
    X -- input data, of shape (input size, number of examples)
    Y -- true "label" vector (1 for blue dot / 0 for red dot), of shape (1, number of examples)
    mini_batch_size -- size of the mini-batches, integer

    Returns:
    mini_batches -- list of synchronous (mini_batch_X, mini_batch_Y)
    """

    np.random.seed(seed)            # To make your "random" minibatches the same as ours
    m = X.shape[1]                  # number of training examples
    mini_batches = []

    # Step 1: Shuffle (X, Y)
    permutation = list(np.random.permutation(m))
    shuffled_X = X[:, permutation]
    shuffled_Y = Y[:, permutation].reshape((1,m))

    # Step 2: Partition (shuffled_X, shuffled_Y). Minus the end case.
    num_complete_minibatches = math.floor(m/mini_batch_size) # number of mini batches of size mini_batch_size in your partitionning
    for k in range(0, num_complete_minibatches):
        ### START CODE HERE ### (approx. 2 lines)
        mini_batch_X = shuffled_X[:,k*mini_batch_size:(k+1)*mini_batch_size]
        mini_batch_Y = shuffled_Y[:,k*mini_batch_size:(k+1)*mini_batch_size]
        ### END CODE HERE ###
        mini_batch = (mini_batch_X, mini_batch_Y)
        mini_batches.append(mini_batch)

    # Handling the end case (last mini-batch < mini_batch_size)
    if m % mini_batch_size != 0:
        ### START CODE HERE ### (approx. 2 lines)
        mini_batch_X = shuffled_X[:,(k+1)*num_complete_minibatches:]
        mini_batch_Y = shuffled_Y[:,(k+1)*num_complete_minibatches:]
        ### END CODE HERE ###
        mini_batch = (mini_batch_X, mini_batch_Y)
        mini_batches.append(mini_batch)

    return mini_batches

3 - Momentum

Because mini-batch gradient descent makes a parameter update after seeing just a subset of examples, the direction of the update has some variance, and so the path taken by mini-batch gradient descent will “oscillate” toward convergence. Using momentum can reduce these oscillations.

Momentum takes into account the past gradients to smooth out the update. We will store the ‘direction’ of the previous gradients in the variable v. Formally, this will be the exponentially weighted average of the gradient on previous steps. You can also think of v as the “velocity” of a ball rolling downhill, building up speed (and momentum) according to the direction of the gradient/slope of the hill.

这里写图片描述

Exercise: Initialize the velocity. The velocity, v, is a python dictionary that needs to be initialized with arrays of zeros. Its keys are the same as those in the grads dictionary, that is:
for l=1,...,L:

v["dW" + str(l+1)] = ... #(numpy array of zeros with the same shape as parameters["W" + str(l+1)])
v["db" + str(l+1)] = ... #(numpy array of zeros with the same shape as parameters["b" + str(l+1)])

Note that the iterator l starts at 0 in the for loop while the first parameters are v[“dW1”] and v[“db1”] (that’s a “one” on the superscript). This is why we are shifting l to l+1 in the for loop.

def initialize_velocity(parameters):
    """
    Initializes the velocity as a python dictionary with:
                - keys: "dW1", "db1", ..., "dWL", "dbL" 
                - values: numpy arrays of zeros of the same shape as the corresponding gradients/parameters.
    Arguments:
    parameters -- python dictionary containing your parameters.
                    parameters['W' + str(l)] = Wl
                    parameters['b' + str(l)] = bl

    Returns:
    v -- python dictionary containing the current velocity.
                    v['dW' + str(l)] = velocity of dWl
                    v['db' + str(l)] = velocity of dbl
    """

    L = len(parameters) // 2 # number of layers in the neural networks
    v = {}

    # Initialize velocity
    for l in range(L):
        ### START CODE HERE ### (approx. 2 lines)
        v["dW" + str(l+1)] = np.zeros(parameters["W"+str(l+1)].shape)
        v["db" + str(l+1)] = np.zeros(parameters["b"+str(l+1)].shape)
        ### END CODE HERE ###

    return v
def update_parameters_with_momentum(parameters, grads, v, beta, learning_rate):
    """
    Update parameters using Momentum

    Arguments:
    parameters -- python dictionary containing your parameters:
                    parameters['W' + str(l)] = Wl
                    parameters['b' + str(l)] = bl
    grads -- python dictionary containing your gradients for each parameters:
                    grads['dW' + str(l)] = dWl
                    grads['db' + str(l)] = dbl
    v -- python dictionary containing the current velocity:
                    v['dW' + str(l)] = ...
                    v['db' + str(l)] = ...
    beta -- the momentum hyperparameter, scalar
    learning_rate -- the learning rate, scalar

    Returns:
    parameters -- python dictionary containing your updated parameters 
    v -- python dictionary containing your updated velocities
    """

    L = len(parameters) // 2 # number of layers in the neural networks

    # Momentum update for each parameter
    for l in range(L):

        ### START CODE HERE ### (approx. 4 lines)
        # compute velocities
        v["dW" + str(l+1)] = beta*v["dW"+str(l+1)]+(1-beta)*grads["dW"+str(l+1)]
        v["db" + str(l+1)] = beta*v["db"+str(l+1)]+(1-beta)*grads["db"+str(l+1)]
        # update parameters
        parameters["W" + str(l+1)] = parameters["W"+str(l+1)] - v["dW"+str(l+1)]*learning_rate
        parameters["b" + str(l+1)] = parameters["b"+str(l+1)] -v["db"+str(l+1)]*learning_rate
        ### END CODE HERE ###

    return parameters, v

4 - Adam

Adam is one of the most effective optimization algorithms for training neural networks. It combines ideas from RMSProp (described in lecture) and Momentum.

How does Adam work?
1. It calculates an exponentially weighted average of past gradients, and stores it in variables v (before bias correction) and vcorrected (with bias correction).
2. It calculates an exponentially weighted average of the squares of the past gradients, and stores it in variables s (before bias correction) and scorrected (with bias correction).
3. It updates parameters in a direction based on combining information from “1” and “2”.

The update rule is, for l=1,...,L:

vdW[l]=β1vdW[l]+(1β1)JW[l]vcorrecteddW[l]=vdW[l]1(β1)tsdW[l]=β2sdW[l]+(1β2)(JW[l])2scorrecteddW[l]=sdW[l]1(β1)tW[l]=W[l]αvcorrecteddW[l]scorrecteddW[l]+ε

where:
- t counts the number of steps taken of Adam
- L is the number of layers
- β1 and β2 are hyperparameters that control the two exponentially weighted averages.
- α is the learning rate
- ε is a very small number to avoid dividing by zero

As usual, we will store all parameters in the parameters dictionary
Exercise: Initialize the Adam variables v,s which keep track of the past information.

Instruction: The variables v,s are python dictionaries that need to be initialized with arrays of zeros. Their keys are the same as for grads, that is:
for l=1,...,L:

v["dW" + str(l+1)] = ... #(numpy array of zeros with the same shape as parameters["W" + str(l+1)])
v["db" + str(l+1)] = ... #(numpy array of zeros with the same shape as parameters["b" + str(l+1)])
s["dW" + str(l+1)] = ... #(numpy array of zeros with the same shape as parameters["W" + str(l+1)])
s["db" + str(l+1)] = ... #(numpy array of zeros with the same shape as parameters["b" + str(l+1)])
def initialize_adam(parameters) :
    """
    Initializes v and s as two python dictionaries with:
                - keys: "dW1", "db1", ..., "dWL", "dbL" 
                - values: numpy arrays of zeros of the same shape as the corresponding gradients/parameters.

    Arguments:
    parameters -- python dictionary containing your parameters.
                    parameters["W" + str(l)] = Wl
                    parameters["b" + str(l)] = bl

    Returns: 
    v -- python dictionary that will contain the exponentially weighted average of the gradient.
                    v["dW" + str(l)] = ...
                    v["db" + str(l)] = ...
    s -- python dictionary that will contain the exponentially weighted average of the squared gradient.
                    s["dW" + str(l)] = ...
                    s["db" + str(l)] = ...

    """

    L = len(parameters) // 2 # number of layers in the neural networks
    v = {}
    s = {}

    # Initialize v, s. Input: "parameters". Outputs: "v, s".
    for l in range(L):
    ### START CODE HERE ### (approx. 4 lines)
        v["dW" + str(l+1)] = np.zeros(parameters["W"+str(l+1)].shape)
        v["db" + str(l+1)] = np.zeros(parameters["b"+str(l+1)].shape)
        s["dW" + str(l+1)] = np.zeros(parameters["W"+str(l+1)].shape)
        s["db" + str(l+1)] = np.zeros(parameters["b"+str(l+1)].shape)
    ### END CODE HERE ###

    return v, s
def update_parameters_with_adam(parameters, grads, v, s, t, learning_rate = 0.01,
                                beta1 = 0.9, beta2 = 0.999,  epsilon = 1e-8):
    """
    Update parameters using Adam

    Arguments:
    parameters -- python dictionary containing your parameters:
                    parameters['W' + str(l)] = Wl
                    parameters['b' + str(l)] = bl
    grads -- python dictionary containing your gradients for each parameters:
                    grads['dW' + str(l)] = dWl
                    grads['db' + str(l)] = dbl
    v -- Adam variable, moving average of the first gradient, python dictionary
    s -- Adam variable, moving average of the squared gradient, python dictionary
    learning_rate -- the learning rate, scalar.
    beta1 -- Exponential decay hyperparameter for the first moment estimates 
    beta2 -- Exponential decay hyperparameter for the second moment estimates 
    epsilon -- hyperparameter preventing division by zero in Adam updates

    Returns:
    parameters -- python dictionary containing your updated parameters 
    v -- Adam variable, moving average of the first gradient, python dictionary
    s -- Adam variable, moving average of the squared gradient, python dictionary
    """

    L = len(parameters) // 2                 # number of layers in the neural networks
    v_corrected = {}                         # Initializing first moment estimate, python dictionary
    s_corrected = {}                         # Initializing second moment estimate, python dictionary

    # Perform Adam update on all parameters
    for l in range(L):
        # Moving average of the gradients. Inputs: "v, grads, beta1". Output: "v".
        ### START CODE HERE ### (approx. 2 lines)
        v["dW" + str(l+1)] = beta1*v["dW"+str(l+1)]+(1-beta1)*grads["dW"+str(l+1)]
        v["db" + str(l+1)] = beta1*v["db"+str(l+1)]+(1-beta1)*grads["db"+str(l+1)]
        ### END CODE HERE ###

        # Compute bias-corrected first moment estimate. Inputs: "v, beta1, t". Output: "v_corrected".
        ### START CODE HERE ### (approx. 2 lines)
        v_corrected["dW" + str(l+1)] = v["dW"+str(l+1)]/(1-beta1**t)
        v_corrected["db" + str(l+1)] = v["db"+str(l+1)]/(1-beta1**t)
        ### END CODE HERE ###

        # Moving average of the squared gradients. Inputs: "s, grads, beta2". Output: "s".
        ### START CODE HERE ### (approx. 2 lines)
        s["dW" + str(l+1)] = beta2*s["dW"+str(l+1)]+(1-beta2)*np.power(grads["dW"+str(l+1)],2)
        s["db" + str(l+1)] = beta2*s["db"+str(l+1)]+(1-beta2)*np.power(grads["db"+str(l+1)],2)
        ### END CODE HERE ###

        # Compute bias-corrected second raw moment estimate. Inputs: "s, beta2, t". Output: "s_corrected".
        ### START CODE HERE ### (approx. 2 lines)
        s_corrected["dW" + str(l+1)] = s["dW"+str(l+1)]/(1-beta2**t)
        s_corrected["db" + str(l+1)] = s["db"+str(l+1)]/(1-beta2**t)
        ### END CODE HERE ###

        # Update parameters. Inputs: "parameters, learning_rate, v_corrected, s_corrected, epsilon". Output: "parameters".
        ### START CODE HERE ### (approx. 2 lines)
        parameters["W" + str(l+1)] = parameters["W"+str(l+1)]-learning_rate*v_corrected["dW"+str(l+1)]/(np.sqrt(s_corrected["dW"+str(l+1)])+epsilon)
        parameters["b" + str(l+1)] = parameters["b"+str(l+1)]-learning_rate*v_corrected["db"+str(l+1)]/(np.sqrt(s_corrected["db"+str(l+1)])+epsilon)
        ### END CODE HERE ###

    return parameters, v, s

5 - Model with different optimization algorithms

Lets use the following “moons” dataset to test the different optimization methods. (The dataset is named “moons” because the data from each of the two classes looks a bit like a crescent-shaped moon.)

train_X, train_Y = load_dataset()

这里写图片描述
We have already implemented a 3-layer neural network. You will train it with:
- Mini-batch Gradient Descent: it will call your function:
- update_parameters_with_gd()
- Mini-batch Momentum: it will call your functions:
- initialize_velocity() and update_parameters_with_momentum()
- Mini-batch Adam: it will call your functions:
- initialize_adam() and update_parameters_with_adam()

def model(X, Y, layers_dims, optimizer, learning_rate = 0.0007, mini_batch_size = 64, beta = 0.9,
          beta1 = 0.9, beta2 = 0.999,  epsilon = 1e-8, num_epochs = 10000, print_cost = True):
    """
    3-layer neural network model which can be run in different optimizer modes.

    Arguments:
    X -- input data, of shape (2, number of examples)
    Y -- true "label" vector (1 for blue dot / 0 for red dot), of shape (1, number of examples)
    layers_dims -- python list, containing the size of each layer
    learning_rate -- the learning rate, scalar.
    mini_batch_size -- the size of a mini batch
    beta -- Momentum hyperparameter
    beta1 -- Exponential decay hyperparameter for the past gradients estimates 
    beta2 -- Exponential decay hyperparameter for the past squared gradients estimates 
    epsilon -- hyperparameter preventing division by zero in Adam updates
    num_epochs -- number of epochs
    print_cost -- True to print the cost every 1000 epochs

    Returns:
    parameters -- python dictionary containing your updated parameters 
    """

    L = len(layers_dims)             # number of layers in the neural networks
    costs = []                       # to keep track of the cost
    t = 0                            # initializing the counter required for Adam update
    seed = 10                        # For grading purposes, so that your "random" minibatches are the same as ours

    # Initialize parameters
    parameters = initialize_parameters(layers_dims)

    # Initialize the optimizer
    if optimizer == "gd":
        pass # no initialization required for gradient descent
    elif optimizer == "momentum":
        v = initialize_velocity(parameters)
    elif optimizer == "adam":
        v, s = initialize_adam(parameters)

    # Optimization loop
    for i in range(num_epochs):

        # Define the random minibatches. We increment the seed to reshuffle differently the dataset after each epoch
        seed = seed + 1
        minibatches = random_mini_batches(X, Y, mini_batch_size, seed)

        for minibatch in minibatches:

            # Select a minibatch
            (minibatch_X, minibatch_Y) = minibatch

            # Forward propagation
            a3, caches = forward_propagation(minibatch_X, parameters)

            # Compute cost
            cost = compute_cost(a3, minibatch_Y)

            # Backward propagation
            grads = backward_propagation(minibatch_X, minibatch_Y, caches)

            # Update parameters
            if optimizer == "gd":
                parameters = update_parameters_with_gd(parameters, grads, learning_rate)
            elif optimizer == "momentum":
                parameters, v = update_parameters_with_momentum(parameters, grads, v, beta, learning_rate)
            elif optimizer == "adam":
                t = t + 1 # Adam counter
                parameters, v, s = update_parameters_with_adam(parameters, grads, v, s,
                                                               t, learning_rate, beta1, beta2,  epsilon)

        # Print the cost every 1000 epoch
        if print_cost and i % 1000 == 0:
            print ("Cost after epoch %i: %f" %(i, cost))
        if print_cost and i % 100 == 0:
            costs.append(cost)

    # plot the cost
    plt.plot(costs)
    plt.ylabel('cost')
    plt.xlabel('epochs (per 100)')
    plt.title("Learning rate = " + str(learning_rate))
    plt.show()

    return parameters

You will now run this 3 layer neural network with each of the 3 optimization methods.

5.1 - Mini-batch Gradient descent

Run the following code to see how the model does with mini-batch gradient descent.

layers_dims = [train_X.shape[0], 5, 2, 1]
parameters = model(train_X, train_Y, layers_dims, optimizer = "gd")

# Predict
predictions = predict(train_X, train_Y, parameters)

# Plot decision boundary
plt.title("Model with Gradient Descent optimization")
axes = plt.gca()
axes.set_xlim([-1.5,2.5])
axes.set_ylim([-1,1.5])
plot_decision_boundary(lambda x: predict_dec(parameters, x.T), train_X, train_Y)
Cost after epoch 0: 0.701104
Cost after epoch 1000: 0.671964
Cost after epoch 2000: 0.637055
Cost after epoch 3000: 0.595732
Cost after epoch 4000: 0.573833
Cost after epoch 5000: 0.550105
Cost after epoch 6000: 0.526063
Cost after epoch 7000: 0.516556
Cost after epoch 8000: 0.497257
Cost after epoch 9000: 0.469029

这里写图片描述

5.2 - Mini-batch gradient descent with momentum

Run the following code to see how the model does with momentum. Because this example is relatively simple, the gains from using momemtum are small; but for more complex problems you might see bigger gains.

# train 3-layer model
layers_dims = [train_X.shape[0], 5, 2, 1]
parameters = model(train_X, train_Y, layers_dims, beta = 0.9, optimizer = "momentum")

# Predict
predictions = predict(train_X, train_Y, parameters)

# Plot decision boundary
plt.title("Model with Momentum optimization")
axes = plt.gca()
axes.set_xlim([-1.5,2.5])
axes.set_ylim([-1,1.5])
plot_decision_boundary(lambda x: predict_dec(parameters, x.T), train_X, train_Y)
Cost after epoch 0: 0.701126
Cost after epoch 1000: 0.672026
Cost after epoch 2000: 0.637154
Cost after epoch 3000: 0.595830
Cost after epoch 4000: 0.573914
Cost after epoch 5000: 0.550181
Cost after epoch 6000: 0.526142
Cost after epoch 7000: 0.516643
Cost after epoch 8000: 0.497434
Cost after epoch 9000: 0.469183

这里写图片描述

5.3 - Mini-batch with Adam mode

Run the following code to see how the model does with Adam.

layers_dims = [train_X.shape[0], 5, 2, 1]
parameters = model(train_X, train_Y, layers_dims, optimizer = "adam")

# Predict
predictions = predict(train_X, train_Y, parameters)

# Plot decision boundary
plt.title("Model with Adam optimization")
axes = plt.gca()
axes.set_xlim([-1.5,2.5])
axes.set_ylim([-1,1.5])
plot_decision_boundary(lambda x: predict_dec(parameters, x.T), train_X, train_Y)
Cost after epoch 0: 0.700276
Cost after epoch 1000: 0.164178
Cost after epoch 2000: 0.131507
Cost after epoch 3000: 0.137168
Cost after epoch 4000: 0.139319
Cost after epoch 5000: 0.136244
Cost after epoch 6000: 0.128836
Cost after epoch 7000: 0.131650
Cost after epoch 8000: 0.127032
Cost after epoch 9000: 0.129201

这里写图片描述

5.4 - Summary

**optimization method** **accuracy** **cost shape**
Gradient descent 79.7% oscillations
Momentum 79.7% oscillations
Adam 94% smoother

Momentum usually helps, but given the small learning rate and the simplistic dataset, its impact is almost negligeable. Also, the huge oscillations you see in the cost come from the fact that some minibatches are more difficult thans others for the optimization algorithm.

Adam on the other hand, clearly outperforms mini-batch gradient descent and Momentum. If you run the model for more epochs on this simple dataset, all three methods will lead to very good results. However, you’ve seen that Adam converges a lot faster.

Some advantages of Adam include:
- Relatively low memory requirements (though higher than gradient descent and gradient descent with momentum)
- Usually works well even with little tuning of hyperparameters (except α)

作者:Hansry 发表于2017/11/10 23:57:50 原文链接
阅读:1 评论:0 查看评论

使用QT和opencv3.3进行目标检测(YOLO)

$
0
0

这篇文章先放个图,准备有时间写



我这里使用的是YOLO2的方法实现的,没有使用cudnn加速,所以速度上慢了点,不过对于高端的显卡来说,我觉得可以做到视屏级的图片产生速度。并且我这里并没有训练好数据,只是一个测试,但是效果还不错。

据说点赞可以加快编辑进度哦^_^!!!

作者:qq_17550379 发表于2017/11/11 9:47:19 原文链接
阅读:8 评论:0 查看评论

centos(5)-yum线上软件管理

$
0
0

上一篇的rpm命令需要知道rpm文件的下载地址才能安装,而yum命令会根据你要安装的程序名,自动到yum服务器上去下载并安装。

查看已安装程序

yum list installed|grep jdk:查看yum已安装的jdk程序,list是列表,installed是已安装的,|grep上篇讲过查找jdk关键字

这正是上篇用rpm安装的jdk,可以从yum程序列表中找到,说明yum所管理的程序就是rpm。虽然后缀不一样,但jdk1.8的名字是一样的。

查看可下载安装程序

yum list available|grep jdk:查看yum命令可下载安装的jdk相关程序,available表示可安装,前提是你还没有安装的程序。


linux中默认的jdk是openjdk,即开源的jdk。而上一篇在oracle官网下载的那个jdk包含openjdk以及闭源组件,其中有些功能是商用的,商用功能需要配置才能开启。

下载安装

wget是一个常用的下载命令,在centos最小镜像中,默认没有这个功能。现在来安装它,先找到可安装的wget名字,然后执行安装。

yum -y install wget:在执行过程中,有些地方需要手动确认输入y,-y就是提前确认。install是安装的意思。稍后会演示如何用wget下载文件。


删除程序

 yum -y remove wget:就是删除刚刚安装的wget,不过前面说了yum管理的程序就是rpm,我是否可以用rpm命令删除?

如下,使用上篇的rpm -e删除了yum安装的wget,之后用yum命令再找不到已安装的wget。更多的rpm相关命令参考上一篇内容。

阿里云镜像仓库

yum默认是从centos的仓库下载软件的,这是国外网站。通过修改配置文件/etc/yum.repos.d/CentOS-Base.repo可以改变仓库,比如从国内的阿里云镜像下载,速度会更快。

mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup

wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo

第一个命令mv是转移命令,将配置文件/etc/yum.repos.d/CentOS-Base.repo转移成一个备份文件,修改了文件名。

第二个命令用刚刚安装的wget下载阿里云的配置文件,-O是将下载的文件保存到指定位置,此时配置文件就被替换了,并且作了备份。


查看文件

使用cat命令可以查看/etc/yum.repos.d/CentOS-Base.repo文件内容。如下可以发现aliyun相关url,具体意思不讲了,不建议自己去改这个文件。


元数据缓存

yum在安装程序时,会根据输入的程序名搜索其下载位置,这需要一定时间。不过可以将服务器上的程序信息缓存到本机,之后便可以直接从缓存中找到相应的下载位置,节省了搜索时间。上面替换了yum仓库以后可以更新一下缓存 

yum makecache 


关于yum更多用法,查看yum --help

作者:wangb_java 发表于2017/11/11 10:01:55 原文链接
阅读:0 评论:0 查看评论
Viewing all 35570 articles
Browse latest View live


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>