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

Java多线程下载框架02:观察者模式通知下载内容状态更新

$
0
0

场景描述

在Java多线程下载框架中,我们需要知道下载状态比如暂停下载,恢复下载,取消下载等状态的通知,而且不仅仅是更新当前页面,在任意页面都能接收到状态变化的更新,所以这里要用到观察者模式。
观察者模式

关于设计模式的详细介绍,我这里有几本电子书籍推荐,公号后台回复”设计模式”,即可获取下载链接。

####那么什么是观察者模式(Observer)?
观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态上发生变化时,会通知所有观察者对象,让他们能够自动更新自己。

举一个例子来说明,牛奶送奶站就是主题,订奶客户为监听者,客户从送奶站订阅牛奶后,会每天收到牛奶。如果客户不想订阅了,可以取消,以后就不会收到牛奶。

为什么要使用观察者模式?为什么不用广播,EventBus,RxBus呢?

广播的劣势

广播是相对消耗时间、空间最多的一种方式,但是大家都知道,广播是四大组件之一,许多系统级的事件都是通过广播来通知的,比如说网络的变化、电量的变化,短信发送和接收的状态,所以,如果与android系统进行相关的通知,还是要选择本地广播;在BroadcastReceiver的 onReceive方法中,可以获得Context 、intent参数,这两个参数可以调用许多的sdk中的方法。

应用发送某个广播时,系统会将广播中的intent与系统中所有注册的BroadcastReceiver进行匹配,如果能匹配成功则调用相关的onReceive函数进行处理。这里存在2个问题:
a、性能问题。每个广播都会与所有BroadcastReceiver进行匹配。
b、安全问题。广播发出去的数据可能被其他应用监听。

因此广播相对于其他的方式而言,广播是重量级的,消耗资源较多的方式。他的优势体现在与sdk连接紧密,如果需要同 android 交互的时候,广播的便捷性会抵消掉它过多的资源消耗,但是如果不同android交互,或者说,只做很少的交互,使用广播是一种浪费。

为什么不使用EventBus,RxBus?

这里不对二者的优缺点进行分析,各有各的好处,看实际需要。因为我们是封装自己的多线程下载框架,所以不能依赖第三方的一些库,因为你不知道用户会使用RxJava还是EventBus。比如你这里用到了RxJava的库,而别人使用你的SDK的之前就集成了EventBus,那不是又要集成RxJava?或者说你这里使用的是Rx1.0,而用户使用的是Rx2.0,所以为了避免不必要的麻烦,我们尽量不被依赖外部资源。

为什么使用观察者模式?
  • 松耦合,观察者增加或删除无需修改主题的代码,只需调用主题对应的增加或者删除的方法即可。
  • 主题只负责通知观察者,但无需了解观察者如何处理通知。举个例子,送奶站只负责送递牛奶,不关心客户是喝掉还是洗脸。
  • 观察者只需等待主题通知,无需观察主题相关的细节。还是那个例子,客户只需关心送奶站送到牛奶,不关心牛奶由哪个快递人员,使用何种交通工具送达。
具体实践

一、创建一个Observable

public class DataChanger extends Observable{

    /**
     * 对外提供一个单列引用,用于注册和取消注册监听
     */
    private static DataChanger mDataChanger;

    public static synchronized DataChanger getInstance(){
        if (null == mDataChanger){
            mDataChanger = new DataChanger();
        }
        return mDataChanger;
    }

    public void notifyDataChange(DownloadEnty mDownloadEnty){
        //Marks this <tt>Observable</tt> object as having been changed
        setChanged();
        //通知观察者 改变的内容 也可不传递具体内容 notifyObservers()
        notifyObservers(mDownloadEnty);
    }
}

主要用于提供注册和删除观察者对象以及通知更新的方法,此处直接继承的是Java提供的Observable,其内部已经实现了

 * addObserver
 * deleteObserver
 * notifyObservers()
 * notifyObservers(Object arg)
 * deleteObservers()
 * setChanged()
 * clearChanged()
 * hasChanged()
 * countObservers()

当内容变化的时候,使用setChanged()和notifyObservers(mDownloadEnty)通知观察者。

二、setChanged和notifyObservers为何物

上述代码中存在这样一处代码setChanged();,如果在通知之前没有调用这个方法,观察者是收不到通知的,这是为什么呢?

这里我们看一下setChanged的源码

 /**
     * Marks this <tt>Observable</tt> object as having been changed; the
     * <tt>hasChanged</tt> method will now return <tt>true</tt>.
     */
    protected synchronized void setChanged() {
        changed = true;
    }

此处把boolen变量changed改为了true

再看notifyObservers源码

 /**
     * If this object has changed, as indicated by the
     * <code>hasChanged</code> method, then notify all of its observers
     * and then call the <code>clearChanged</code> method to indicate
     * that this object has no longer changed.
     * <p>
     * Each observer has its <code>update</code> method called with two
     * arguments: this observable object and the <code>arg</code> argument.
     *
     * @param   arg   any object.
     * @see     java.util.Observable#clearChanged()
     * @see     java.util.Observable#hasChanged()
     * @see     java.util.Observer#update(java.util.Observable, java.lang.Object)
     */
    public void notifyObservers(Object arg) {
        /*
         * a temporary array buffer, used as a snapshot of the state of
         * current Observers.
         */
        Observer[] arrLocal;

        synchronized (this) {
            /* We don't want the Observer doing callbacks into
             * arbitrary Observables while holding its own Monitor.
             * The code where we extract each Observable from
             * the ArrayList and store the state of the Observer
             * needs synchronization, but notifying observers
             * does not (should not).  The worst result of any
             * potential race-condition here is that:
             *
             * 1) a newly-added Observer will miss a
             *   notification in progress
             * 2) a recently unregistered Observer will be
             *   wrongly notified when it doesn't care
             */
            if (!hasChanged())
                return;

            arrLocal = observers.toArray(new Observer[observers.size()]);
            clearChanged();
        }

        for (int i = arrLocal.length-1; i>=0; i--)
            arrLocal[i].update(this, arg);
    }

可以看到

  if (!hasChanged())
       return;

所以这就是为什么通知更新前一定要调用setChanged的原因

但是为什么要加入这样一个开关呢?可能原因大致有三点

1.筛选有效通知,只有有效通知可以调用setChanged。比如,我的微信朋友圈一条状态,好友A点赞,后续该状态的点赞和评论并不是每条都通知A,只有A的好友触发的操作才会通知A。

2.便于撤销通知操作,在主题中,我们可以设置很多次setChanged,但是在最后由于某种原因需要取消通知,我们可以使用clearChanged轻松解决问题。

3.主动权控制,由于setChanged为protected,而notifyObservers方法为public,这就导致存在外部随意调用notifyObservers的可能,但是外部无法调用setChanged,因此真正的控制权应该在主题这里。

三、创建Observer

/**
 * Created by chenshouyin on 2017/10/25.
 * 我的博客:http://blog.csdn.net/e_inch_photo
 * 我的Github:https://github.com/chenshouyin
 */

public abstract class DataWhatcher implements Observer {
    @Override
    public void update(Observable observable, Object data) {
        if (data instanceof DownloadEnty){
            notifyDataChange(data);
        }
    }

    public abstract void notifyDataChange(Object data);

}

为那些在目标发生改变时需获得通知的类定义个更新的接口,这里对接口再进行了判断,对外提供了notifyDataChange抽象方法,外部可在此抽象方法在获取到更新的回调以及更新的对象。

四、添加和取消观察者

private DataWhatcher dataWhatcher = new DataWhatcher() {

        @Override
        public void notifyDataChange(Object data) {
            downloadEnty = (DownloadEnty) data;
            if (downloadEnty.downloadStatus == DownloadEnty.DownloadStatus.downloading){
                LogUtil.e("download","===notifyDataChange===downloading"+downloadEnty.currentLenth);
            }else if (downloadEnty.downloadStatus == DownloadEnty.DownloadStatus.downloadcomplete){
                LogUtil.e("download","===notifyDataChange===downloadcomplete");
            }else if (downloadEnty.downloadStatus == DownloadEnty.DownloadStatus.downloadcansel){
                downloadEnty = null;
                LogUtil.e("download","===notifyDataChange===downloadcansel");
            }else if (downloadEnty.downloadStatus == DownloadEnty.DownloadStatus.downloadpause){
                LogUtil.e("download","===notifyDataChange===downloadpause");
            }else{
                LogUtil.e("download","===notifyDataChange===下载进度"+downloadEnty.currentLenth);
            }

        }
    };
  @Override
    protected void onResume() {
        super.onResume();
        DownloadManager.getInstance().addObserve(dataWhatcher);
    }

    @Override
    protected void onStop() {
        super.onStop();
        DownloadManager.getInstance().removeObserve(dataWhatcher);
    }

五、运行效果

运行效果

六、观察者模式使用总结

从上面可以看出,实际上观察者和被观察者是通过接口回调来通知更新的,首先创建一个观察者(数据监听)实例并实现数据变化接口,通过注册监听将实例传入被观察者(数据变化),当被观察者数据变化的时候使用该实例的接口回传状态。了解原理之后,我们可以利用观察者模式自定义实现。

拿微信公众号来举例,假设微信用户就是观察者,微信公众号是被观察者,有多个的微信用户关注了 陈守印同学 这个公众号,当这个公众号更新时就会通知这些订阅的微信用户。我们来看看用代码如何实现:

1.抽象观察者(Observer)

public interface Observer {
    public void update(String message);
}

2.具体观察者(ConcrereObserver)

微信用户是观察者,里面实现了更新的方法:

里面定义了一个更新的方法:

public class WeixinUser implements Observer {
    // 微信用户名字
    private String name;
    public WeixinUser(String name) {
        this.name = name;
    }
    @Override
    public void update(String message) {
        System.out.println(name + ":" + message);
    }
}

3.抽象被观察者(Subject)

抽象主题,提供了attach、detach、notify三个方法:

public interface Subject {
    /**
     * 增加订阅者
     * @param observer
     */
    public void attach(Observer observer);
    /**
     * 删除订阅者
     * @param observer
     */
    public void detach(Observer observer);
    /**
     * 通知订阅者更新消息
     */
    public void notify(String message);
}

4.具体被观察者(ConcreteSubject)

微信公众号是具体主题(具体被观察者),里面存储了订阅该公众号的微信用户,并实现了抽象主题中的方法:

public class SubscriptionSubject implements Subject {
    //储存订阅公众号的微信用户
    private List<Observer> weixinUserlist = new ArrayList<Observer>();

    @Override
    public void attach(Observer observer) {
        weixinUserlist.add(observer);
    }

    @Override
    public void detach(Observer observer) {
        weixinUserlist.remove(observer);
    }

    @Override
    public void notify(String message) {
        for (Observer observer : weixinUserlist) {
            observer.update(message);
        }
    }
}

5.客户端调用

java
public class Client {
public static void main(String[] args) {
SubscriptionSubject mSubscriptionSubject=new SubscriptionSubject();
//创建微信用户
WeixinUser user1=new WeixinUser("陈守印同学公众号粉丝A");
WeixinUser user2=new WeixinUser("陈守印同学公众号粉丝B");
WeixinUser user3=new WeixinUser("陈守印同学公众号粉丝C");
WeixinUser user4=new WeixinUser("陈守印同学公众号粉丝D");
//订阅公众号
mSubscriptionSubject.attach(user1);
mSubscriptionSubject.attach(user2);
mSubscriptionSubject.attach(user3);
mSubscriptionSubject.attach(user4);
//公众号更新发出消息给订阅的微信用户
mSubscriptionSubject.notify("陈守印同学公众号的文章更新啦");
}
}

6.运行结果

陈守印同学公众号粉丝A:陈守印同学公众号的文章更新啦
陈守印同学公众号粉丝B:陈守印同学公众号的文章更新啦
陈守印同学公众号粉丝C:陈守印同学公众号的文章更新啦
陈守印同学公众号粉丝D:陈守印同学公众号的文章更新啦

6.观察者模式优缺点
* 解除耦合,让耦合的双方都依赖于抽象,从而使得各自的变换都不会影响另一边的变换。
* 如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。

公号后台回复”设计模式”,获取设计模式书籍

设计模式

后续文章持续更新中,微信扫码下方二维码免费关注!上一篇:Java多线程下载01:多线程的好处以及断点续传原理
点此查看全部最新文章


我的博客
我的简书
我的GitHub,喜欢的话给个star吧

作者:e_Inch_Photo 发表于2017/11/9 16:30:41 原文链接
阅读:230 评论:0 查看评论

React-Native生命周期的触发场景和一些小建议

$
0
0

转载请注明出处:王亟亟的大牛之路

把王者荣耀删了后这几天回到了举铁,遛鸟,打球,睡觉的正常节奏,然后卡了看之前写的一些东西,发现生命周期没写,那么就补一篇(虽然搜搜一大堆,但是残缺总不合适,再加点建议点那就和别人的不同了)

老规矩案例地址:https://github.com/ddwhan0123/Useful-Open-Source-Android(最近把时间选择器/日历这一块更新了好多内容)


React-Native控件的生命周期

方法名 作用 调用次数
constructor 构造函数,初始化需要的state 1次
componentWillMount 控件渲染前触发 1次
rander 渲染控件的方法 多次
componentDidMount 控件渲染后触发 1次
componentWillReceiveProps 组件接收到新的props时被调用 多次
shouldComponentUpdate 当组件接收到新的props和state时被调用 多次
componentWillUpdate props或者state改变,并且此前的shouldComponentUpdate方法返回为 true会调用该方法 多次
componentDidUpdate 组件重新渲染完成后会调用此方法 多次
componentWillUnmount 组件卸载和销毁之前被调用 1次

各个生命周期触发过程

这里写图片描述

上图为demo效果图

初次加载
这里写图片描述

依次触发了父控件的构造函数,componentwillMount,render,子控件的构造函数,子控件的componentwillMount,render,componentwillMount,最后才是父控件的componentwillMount

可以看出,初次的渲染周期是从外向内逐步渲染,内部完成后才算整体结束。


UI 刷新
这里写图片描述

点击事件触发了页面的状态机放生了变化,我们来看看每一步做了什么

1.首先是用户的点击触发onPress={this.addPress}

2.这方法做了一个事,把事件和值传递给reducer this.props.dispatch(add(this.state.intvalue));

3.reducer把必然的结果算完后有了个新的nextProps.result并且触发shouldComponentUpdate(nextProps, nextState)方法

4.比对值确实不同所以shouldComponentUpdate(nextProps, nextState)方法的返回值为true

5.因为返回值为true所以主控件触发render()方法 (主控件没复写componentWillUpdate()和componentDidUpdate()两个方法)

6.因为父控件给子控件传递的值正好也变了也就触发了子控件的刷新方法

 <SonComponent sonValue={this.state.showText.data + this.state.intvalue}/>

然后走了一圈一摸一样的流程完成了刷新


卸载姿势

这里写图片描述
卸载方法也是从外向内触发,点Home键不会触发(至少当前不触发),双击返回键会触发(任务中心关闭也没触发)。

这次的demo在上次redux的demo基础上做的修改,主要是阐明子组件和父组件的关系,源码地址:https://github.com/ddwhan0123/ReduxDemo


在各个生命周期建议做的事

constructor()方法里初始化state
componentDidMount()方法里跑网/耗时操作
componentWillMount()可在方法里对state进行最后的修改

注意,不要在 constructor 或者 render 里 setState(),這是因为 constructor 已含 this.state={} ,而 render 里 setState 会造成setState -> render -> setState -> render
能做的setState,只要是render前,就放在componentWillMount,render后,就放在 componentDidMount。這两个 function 是 react lifecycle 中,最常使用的两个。当然啦,还有其它的部分,那就交给客官们自行研究和推敲它们的使用时机咯!

有问题可以微信联系,当然得注明来意,不添加备注不会通过,谢谢(私人微信 非诚勿扰)
这里写图片描述

以后会同步微信发布,扫麦麦的码可以关注
这里写图片描述

作者:ddwhan0123 发表于2017/11/9 17:54:25 原文链接
阅读:203 评论:0 查看评论

洛谷3941 入阵曲

$
0
0

标签:模拟,前缀和

题目背景
pdf题面和大样例链接:http://pan.baidu.com/s/1cawM7c 密码:xgxv
丹青千秋酿,一醉解愁肠。
无悔少年枉,只愿壮志狂。

题目描述
小 F 很喜欢数学,但是到了高中以后数学总是考不好。

有一天,他在数学课上发起了呆;他想起了过去的一年。一年前,当他初识算法竞赛的 时候,觉得整个世界都焕然一新。这世界上怎么会有这么多奇妙的东西?曾经自己觉得难以解决的问题,被一个又一个算法轻松解决。

小 F 当时暗自觉得,与自己的幼稚相比起来,还有好多要学习的呢。

一年过去了,想想都还有点恍惚。

他至今还能记得,某天晚上听着入阵曲,激动地睡不着觉,写题写到鸡鸣时分都兴奋不 已。也许,这就是热血吧。

也就是在那个时候,小 F 学会了矩阵乘法。让两个矩阵乘几次就能算出斐波那契数列的第 10^100项,真是奇妙无比呢。

不过,小 F 现在可不想手算矩阵乘法——他觉得好麻烦。取而代之的,是一个简单的小问题。他写写画画,画出了一个 n \times mn×m 的矩阵,每个格子里都有一个不超过 kk 的正整数。

小 F 想问问你,这个矩阵里有多少个不同的子矩形中的数字之和是 kk 的倍数? 如果把一个子矩形用它的左上角和右下角描述为(x1,y1,x2,y2)(x1 ,y1,x2 ,y2 ),其中x1<=x2,y1<=y2 ;那么,我们认为两个子矩形是不同的,当且仅当他们以(x1,y1,x2,y2)表示时不同;也就是 说,只要两个矩形以 (x1,y1,x2,y2)表示时相同,就认为这两个矩形是同一个矩形,你应该 在你的答案里只算一次。

输入输出格式

输入格式:
从标准输入中读入数据。

输入第一行,包含三个正整数 n,m,k。

输入接下来 n 行,每行包含 m个正整数,第 i行第 j 列表示矩阵中第 i 行第 j列 中所填的正整数 a[ i ][ j ]
输出格式:
输出到标准输出中。
输入一行一个非负整数,表示你的答案。

输入输出样例
输入样例#1:
2 3 2
1 2 1
2 1 2
输出样例#1:
6
说明
【样例 1 说明】
这些矩形是符合要求的: (1, 1, 1, 3),(1, 1,2, 2),(1, 2, 1, 2),(1, 2, 2, 3),(2, 1, 2, 1),(2, 3, 2, 3)。
子任务会给出部分测试数据的特点。如果你在解决题目中遇到了困难,可以尝试只解 决一部分测试数据。每个测试点的数据规模及特点如下表:


 

分析:

实质上K倍区间的方法

O(n^4)用双重前缀和优化

O(n^3)可以枚举x1,x2两行并枚举y列   把这些压成一个数,之后进行枚举统计

对于任意一段区间[l,r]的和就是s[r]-s[l-1].

(sum[r]-sum[l-1])%k 保证了[l,r]这段区间要么%k等于0 要么比k小.

等于0说明这段区间正好是k的倍数然后通过前缀和相同的数据来判断出剩下的k的倍数:(sum[r]-sum[l-1])%k== 0.

变形后就是:sum[r]%k==sum[l-1]%k

Code

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<algorithm>
#define rep(i,a,b) for(register int i=a;i<=b;i++)
#define dep(i,a,b) for(register int i=a;i>=b;i--)
#define ll long long
#define mem(x,num) memset(x,num,sizeof x)
#ifdef WIN32
#define LL "%I64d\n"
#else
#define LL "%lld\n"
#endif
using namespace std;
inline ll read()
{
    ll f=1,x=0;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
const int maxn=406,maxx=1e6+6;
ll n,m,k,x,s[maxn][maxn],b[maxx],cnt[maxx]={0},ans=0;
int main()
{
	n=read(),m=read(),k=read();
	rep(i,1,n)
		rep(j,1,m)x=read(),s[i][j]=s[i-1][j]+s[i][j-1]+x-s[i-1][j-1];
	rep(i,0,n-1)
		rep(j,i+1,n){
			cnt[0]=1;
			rep(p,1,m){
				b[p]=(s[j][p]-s[i][p]+k)%k;
				ans+=cnt[b[p]]++;
			}
			rep(p,1,m)cnt[b[p]]=0;
		}
	printf(LL,ans);
	return 0;
}
	


作者:qwerty1125 发表于2017/11/9 17:58:33 原文链接
阅读:171 评论:0 查看评论

洛谷3942 将军令

$
0
0

标签:贪心,树状DP

题目背景

pdf题面和大样例链接:http://pan.baidu.com/s/1cawM7c 密码:xgxv

 历史/落在/赢家/之手

 至少/我们/拥有/传说

 谁说/败者/无法/不朽

 拳头/只能/让人/低头

 念头/却能/让人/抬头

 抬头/去看/去爱/去追

 你心中的梦  

题目描述

又想起了四月。

如果不是省选,大家大概不会这么轻易地分道扬镳吧? 只见一个又一个昔日的队友离开了机房。

凭君莫话封侯事,一将功成万骨枯。

梦里,小 F 成了一个给将军送密信的信使。

现在,有两封关乎国家生死的密信需要送到前线大将军帐下,路途凶险,时间紧迫。小 F 不因为自己的祸福而避趋之,勇敢地承担了这个任务。

不过,小 F 实在是太粗心了,他一不小心把两封密信中的一封给弄掉了。

小 F 偷偷打开了剩下的那封密信。他 发现一副十分详细的地图,以及几句批文——原来 这是战场周围的情报地图。他仔细看后发现,在这张地图上标记了 n 个从 1 到 n 标号的 驿站,n − 1 条长度为 1 里的小道,每条小道双向连接两个不同的驿站,并且驿站之间可以通过小道两两可达。

小 F 仔细辨认着上面的批注,突然明白了丢失的信的内容了。原来,每个驿站都可以驻扎一个小队,每个小队可以控制距离不超过 k 里的驿站。如果有驿站没被控制,就容易产 生危险——因此这种情况应该完全避免。而那封丢失的密信里,就装着朝廷数学重臣留下的 精妙的排布方案,也就是用了最少的小队来控制所有驿站。

小 F 知道,如果能计算出最优方案的话,也许他就能够将功赎过,免于死罪。他找到了你,你能帮帮他吗? 当然,小 F 在等待你的支援的过程中,也许已经从图上观察出了一些可能会比较有用的 性质,他会通过一种特殊的方式告诉你。

输入输出格式

输入格式:

从标准输入中读入数据。

输入第 1 行一个正整数 n,k,t,代表驿站数,一支小队能够控制的最远距离,以及特殊性质所代表的编号。关于特殊性质请参照数据范围。

输入第 2 行至第 n 行,每行两个正整数ui​,表示在 ui 和 vi 间,有一条长度为一里的小道。

输出格式:

输出到标准输出中。

输出一行,为最优方案下需要的小队数。

输入输出样例

输入样例#1: 复制

4 1 0

1 2

1 3

1 4

输出样例#1: 复制

1

 

输入样例#2: 复制

6 1 0

1 2

1 3

1 4

4 5

4 6

输出样例#2: 复制

2

说明

【样例 1 说明】

如图。由于一号节点到周围的点距离均是 1,因此可以控制所有驿站。

【样例 2 说明】

如图,和样例 1 类似。


子任务会给出部分测试数据的特点。如果你在解决题目中遇到了困难,可以尝试只解 决一部分测试数据。

关于 t 的含义如下: t = 0:该测试点没有额外的特殊性质; t = 1:保证最多 8 个点的所连接的小道超过 1 条; t = 2:保证所有点到 1号点的距离不超过 2。

每个测试点的数据规模及特点如下表

 

题意:给定一棵树,选择一个点可以控制距离不超过

作者:qwerty1125 发表于2017/11/9 18:55:47 原文链接
阅读:156 评论:0 查看评论

【GitChat】达人课推荐:React 技术栈|Gradle 从入门到实战|GitHub 入味儿

$
0
0

达人课是GitChat的一款轻阅读产品,由特约讲师独家发布。每一个课程你都可获得6-12篇的深度文章,同时可在读者圈与讲师互动交流。GitChat达人课,让技术分享更简单。


如何从零学习 React 技术栈

余博伦 · 前端颜值担当

这里写图片描述

  • 本课程共六篇文章

在学会 React 之后,你的能力将不止局限于浏览器,React 还可以拓宽到使用 React Native 开发原生应用,以及使用 ReactVR 开发虚拟现实等各个领域。除了示例代码的讲解之外,本课程还会对个别核心概念的原理进行讲解介绍,并用实际案例 TodoList 应用来展示。

点击阅读全文


Gradle 从入门到实战

杨彪 · 技术总监及合伙人

这里写图片描述

本期达人课,我将带大家从最基础的 Groovy 语法开始学习,再一起探讨 Gradle 强大的特性,最后使用 Jenkins 和 Gradle 实现自动化的集成、打包和部署的实战练习。

通过本课程学习,可以全面了解 Gradle 的具体使用方法和实际项目的实战经验。

点击阅读全文


GitQ: GitHub 入味儿

Zoom.Quiet · Python 中文社区联合创始人

这里写图片描述

笔者从 GitHub 发布之前就一直在使用各种代码托管平台, 对于这类服务有足够的体验,愿意同大家分享,共同享受这个世界。

所以,本课程本质上是一老程序猿的私人吐糟集锦,目标不是教授使用 GitHub ,而是希望以一种聊天的形式,将使用 GitHub 的感觉传达给从未注册过的新人,以便每个人能自然的进入、享受、成长,分享编程的乐趣。

点击阅读全文


GitChat 是一款基于微信平台的 IT 阅读/写作互动产品。我们的目的是通过这款产品改变 IT 知识的学习方式,让专业读者获得自主选择权,让知识分享者获得收益。

关于GitChat 你想知道的都在这

作者:blogdevteam 发表于2017/11/9 19:11:28 原文链接
阅读:553 评论:0 查看评论

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

$
0
0

这里写图片描述

import numpy as np
from testCases import *
from gc_utils import sigmoid,relu,dictionary_to_vector,vector_to_dictionary,gradients_to_vector
from testCases import gradient_check_n_test_case

Gradient_check.py

import numpy as np
from testCases import *
from gc_utils import sigmoid,relu,dictionary_to_vector,vector_to_dictionary,gradients_to_vector
from testCases import gradient_check_n_test_case
def gradient_check(x,theta,epsilon= 1e-7):
    J=x*theta
    dtheta=x
    gradapprox=(x*(theta+epsilon)-x*(theta-epsilon))/(2*epsilon)
    grad=dtheta
    numerator=np.linalg.norm(grad-gradapprox)
    denomitor=np.linalg.norm(grad)+np.linalg.norm(gradapprox)
    difference=numerator/denomitor
    if difference < epsilon:
        print "the gradient is correct"
    elif difference >= epsilon:
        print "the gradient is not so ideal"
    return J,difference

x,theta=2,4
J,difference = gradient_check(x,theta)
print("difference = "+str(difference))


def forward_propagation_n(X,Y,parameters):
    m=X.shape[1]
    W1=parameters["W1"]
    b1=parameters["b1"]
    W2=parameters["W2"]
    b2=parameters["b2"]  
    W3=parameters["W3"]
    b3=parameters["b3"]

    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)
    cost=(-1.0/m)*(np.sum(Y*np.log(A3)+(1-Y)*np.log(1-A3)))
    cache=(Z1,A1,W1,b1,Z2,A2,W2,b2,Z3,A3,W3,b3)
    return cost,cache

def backward_propagation_n(X,Y,cache):
    (Z1,A1,W1,b1,Z2,A2,W2,b2,Z3,A3,W3,b3)=cache
    m=X.shape[1]
    grads={}
    dZ3=A3-Y
    dW3=(1.0/m)*np.dot(dZ3,A2.T)
    db3=(1.0/m)*np.sum(dZ3,axis=1,keepdims=True)

    dA2=np.dot(W3.T,dZ3)   
    dZ2=np.multiply(dA2,np.int64(Z2>0))
    dW2=(1.0/m)*np.dot(dZ2,A1.T)
    db2=(1.0/m)*np.sum(dZ2,axis=1,keepdims=True)

    dA1=np.dot(W2.T,dZ2)   
    dZ1=np.multiply(dA1,np.int64(Z1>0))
    dW1=(1.0/m)*np.dot(dZ1,X.T)
    db1=(1.0/m)*np.sum(dZ1,axis=1,keepdims=True)

    grads={"dZ3": dZ3, "dW3": dW3, "db3": db3,
                 "dA2": dA2, "dZ2": dZ2, "dW2": dW2, "db2": db2,
                 "dA1": dA1, "dZ1": dZ1, "dW1": dW1, "db1": db1}
    return grads

def implement_gradient_check_n(X,Y,grads,parameter,epsilon=1e-6):
    parameters_value,_=dictionary_to_vector(parameter)
    grads=gradients_to_vector(grads)
    num_parameters=parameters_value.shape[0]
    J_plus=np.zeros((num_parameters,1))
    J_minus=np.zeros((num_parameters,1))
    gradapprox=np.zeros((num_parameters,1))
    for i in range(num_parameters):
        thetaplus=np.copy(parameters_value)
        thetaplus[i][0]=thetaplus[i][0]+epsilon
        J_plus[i],_=forward_propagation_n(X,Y,vector_to_dictionary(thetaplus))
        thetaminus=np.copy(parameters_value)
        thetaminus[i][0]=thetaminus[i][0]-epsilon
        J_minus[i],_=forward_propagation_n(X,Y,vector_to_dictionary(thetaminus))

        gradapprox[i]=(J_plus[i]-J_minus[i])/(2*epsilon)

    difference=np.linalg.norm(grads-gradapprox)/(np.linalg.norm(grads)+np.linalg.norm(gradapprox))
    if difference<1e-6:
        print ("\033[92m" + "Your backward propagation works perfectly fine! difference = " + str(difference) + "\033[0m")
    else:
        print ("\033[93m" + "There is a mistake in the backward propagation! difference = " + str(difference) + "\033[0m")

    return difference

X, Y, parameters = gradient_check_n_test_case()
print parameters
cost, cache = forward_propagation_n(X, Y, parameters)
gradients = backward_propagation_n(X, Y, cache)
print gradients
difference = implement_gradient_check_n(X, Y,gradients,parameters)

gc_utils.py:

import numpy as np
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 dictionary_to_vector(parameters):
    """
    Roll all our parameters dictionary into a single vector satisfying our specific required shape.
    """
    keys = []
    count = 0
    for key in ["W1", "b1", "W2", "b2", "W3", "b3"]:

        # flatten parameter
        new_vector = np.reshape(parameters[key], (-1,1))
        keys = keys + [key]*new_vector.shape[0]        
        if count == 0:
            theta = new_vector
        else:
            theta = np.concatenate((theta, new_vector), axis=0)
        count = count + 1
    return theta, keys

def vector_to_dictionary(theta):
    """
    Unroll all our parameters dictionary from a single vector satisfying our specific required shape.
    """
    parameters = {}
    parameters["W1"] = theta[:20].reshape((5,4))
    parameters["b1"] = theta[20:25].reshape((5,1))
    parameters["W2"] = theta[25:40].reshape((3,5))
    parameters["b2"] = theta[40:43].reshape((3,1))
    parameters["W3"] = theta[43:46].reshape((1,3))
    parameters["b3"] = theta[46:47].reshape((1,1))
    return parameters

def gradients_to_vector(gradients):
    """
    Roll all our gradients dictionary into a single vector satisfying our specific required shape.
    """    
    count = 0
    for key in ["dW1", "db1", "dW2", "db2", "dW3", "db3"]:
        # flatten parameter
        new_vector = np.reshape(gradients[key], (-1,1))        
        if count == 0:
            theta = new_vector
        else:
            theta = np.concatenate((theta, new_vector), axis=0)
        count = count + 1
    return theta

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

def gradient_check(x,theta,epsilon= 1e-7):  #定义一元梯度检测函数
    J=x*theta    #函数主体
    dtheta=x     #对theta求导
    gradapprox=(x*(theta+epsilon)-x*(theta-epsilon))/(2*epsilon)   #梯度估计,就是上述公式(1)
    grad=dtheta
    numerator=np.linalg.norm(grad-gradapprox)  #其中np.linalg.norm()相当于二范数
    denomitor=np.linalg.norm(grad)+np.linalg.norm(gradapprox)   
    difference=numerator/denomitor  #上述公式(2)
    if difference < epsilon:   #梯度误差估计,小于epsilon说明正确
        print "the gradient is correct"
    elif difference >= epsilon:
        print "the gradient is not so ideal"
    return J,difference

x,theta=2,4
J,difference = gradient_check(x,theta)
print("difference = "+str(difference))

Expected output:

the gradient is correct
difference = 2.91933588329e-10

这里写图片描述

def forward_propagation_n(X, Y, parameters): 
    """
    Implements the forward propagation (and computes the cost) presented in Figure 3.

    Arguments:
    X -- training set for m examples
    Y -- labels for m examples 
    parameters -- python dictionary containing your parameters "W1", "b1", "W2", "b2", "W3", "b3":
                    W1 -- weight matrix of shape (5, 4)
                    b1 -- bias vector of shape (5, 1)
                    W2 -- weight matrix of shape (3, 5)
                    b2 -- bias vector of shape (3, 1)
                    W3 -- weight matrix of shape (1, 3)
                    b3 -- bias vector of shape (1, 1)

    Returns:
    cost -- the cost function (logistic cost for one example)
    """

    # retrieve parameters
    m = X.shape[1]
    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)

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

    cache = (Z1, A1, W1, b1, Z2, A2, W2, b2, Z3, A3, W3, b3)

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

    Arguments:
    X -- input datapoint, of shape (input size, 1)
    Y -- true "label"
    cache -- cache output from forward_propagation_n()

    Returns:
    gradients -- A dictionary with the gradients of the cost 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 = A3 - Y
    dW3 = 1./m * np.dot(dZ3, A2.T)
    db3 = 1./m * np.sum(dZ3, axis=1, keepdims = True)

    dA2 = np.dot(W3.T, dZ3)
    dZ2 = np.multiply(dA2, np.int64(A2 > 0))
    dW2 = 1./m * np.dot(dZ2, A1.T) 
    db2 = 1./m * np.sum(dZ2, axis=1, keepdims = True)

    dA1 = np.dot(W2.T, dZ2)
    dZ1 = np.multiply(dA1, np.int64(A1 > 0))
    dW1 = 1./m * np.dot(dZ1, X.T)
    db1 = 1./m * 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 implement_gradient_check_n(X,Y,grads,parameter,epsilon=1e-6):  #定义n维梯度检测函数
    parameters_value,_=dictionary_to_vector(parameter)  #将parameter字典reshape成向量
    grads=gradients_to_vector(grads)
    num_parameters=parameters_value.shape[0]
    J_plus=np.zeros((num_parameters,1))
    J_minus=np.zeros((num_parameters,1))
    gradapprox=np.zeros((num_parameters,1))
    for i in range(num_parameters):
        thetaplus=np.copy(parameters_value)  #将parameter_value复制给thetaplus
        thetaplus[i][0]=thetaplus[i][0]+epsilon #thetaplus+epsilon
        J_plus[i],_=forward_propagation_n(X,Y,vector_to_dictionary(thetaplus)) #求出代价函数值
        thetaminus=np.copy(parameters_value)
        thetaminus[i][0]=thetaminus[i][0]-epsilon
        J_minus[i],_=forward_propagation_n(X,Y,vector_to_dictionary(thetaminus))#同样的,求出第i个元素减去epsilon之后得到的代价函数值

        gradapprox[i]=(J_plus[i]-J_minus[i])/(2*epsilon)  #梯度估计

    difference=np.linalg.norm(grads-gradapprox)/(np.linalg.norm(grads)+np.linalg.norm(gradapprox))
    if difference<1e-6:
        print ("\033[92m" + "Your backward propagation works perfectly fine! difference = " + str(difference) + "\033[0m")
    else:
        print ("\033[93m" + "There is a mistake in the backward propagation! difference = " + str(difference) + "\033[0m")

    return difference

X, Y, parameters = gradient_check_n_test_case()
print parameters
cost, cache = forward_propagation_n(X, Y, parameters)
gradients = backward_propagation_n(X, Y, cache)
print gradients
difference = implement_gradient_check_n(X, Y,gradients,parameters)

Expected output

Your backward propagation works perfectly fine! difference = 8.26588224678e-09
作者:Hansry 发表于2017/11/9 19:36:01 原文链接
阅读:133 评论:0 查看评论

Android O 拨打电话流程之呼出

$
0
0

DialtactsActivity.java模块是负责电话拨打界面
DialpadFragment.java:负责拨号盘的类文件
这里写图片描述

拨打电话界面,在onClick方法中点击dialpad_floating_action_button按钮进行拨打电话,进入handleDialButtonPressed()中处理拨打电话 DialerUtils.startActivityWithErrorToast
在CallIntentBuilder类的build方法中,
Intent intent = new Intent(Intent.ACTION_CALL, uri);
Intent.ACTION_CALL =”android.intent.action.CALL”

这里写图片描述

AndroidManifest.xml文件中有定义,对应文件为UserCallActivity
这里写图片描述
这里写图片描述

startActivityWithErrorToast中调用 placeCallOrMakeToast,最终调用
这里写图片描述

TelecomManager.java中的placeCall
这里写图片描述
最终进入TelecomManager.placeCall中,该方法继续调用ITelecomService,具体实现在类TelecomServiceImpl中
这里写图片描述
/packages/services/Telecomm/src/com/android/server/telecom/components/UserCallIntentProcessor.java
UserCallIntentProcessor.java中处理processIntent去发送到Activity
这里写图片描述

在processOutgoingCallIntent中处理,拨号权限,看是否直接拒绝,都是通过 sendBroadcastToReceiver(intent)发送出去
这里写图片描述

这里写图片描述

/packages/services/Telecomm/src/com/android/server/telecom/components/PrimaryCallReceiver.java

进入广播接收器PrimaryCallReceiver中通过调用getTelecomSystem中的getCallIntentProcessor来获取CallIntentProcessor对象,该对象的创建在TelecomSystem的构造方法中
这里写图片描述

这里写图片描述

/packages/services/Telecomm/src/com/android/server/telecom/CallIntentProcessor.java

该processIntent中处理,该类中的CallsManager是从哪儿来的呢?
这是CallIntentProcessor构造方法中传入进来的,这就要追寻到CallIntentProcessor创建来的,最终发现CallManager也是在TelecomSystem中创造的!
这里写图片描述

processOutgoingCallIntent中的处理部分,通过CallManager创建Call
这里写图片描述
这里写图片描述

/packages/services/Telecomm/src/com/android/server/telecom/CallsManager.java

这里写图片描述

主要完成的功能是:
创建Call对象
获取当前激活的卡的列表
获取当前使用哪一张卡呼出
设置当前通话账户: call.setTargetPhoneAccount(phoneAccountHandle);
设置当前call正在连接,将call加入列表 addCall(call);
这里写图片描述

这里写图片描述

当调用CallsManager的startOutgoingCall结束,返回call对象,之后调用
/packages/services/Telecomm/src/com/android/server/telecom/NewOutgoingCallIntentBroadcaster.java

processIntent()方法中最终调用 mCallsManager.placeOutgoingCall,该mCallsManager对象还是在之前创建的,placeOutgoingCall继续调用 call.startCreateConnection(mPhoneAccountRegistrar);建立连接

packages/services/Telecomm/src/com/android/server/telecom/Call.java
这里写图片描述

/packages/services/Telecomm/src/com/android/server/telecom/CreateConnectionProcessor.java
处理连接请求
这里写图片描述

/packages/services/Telecomm/src/com/android/server/telecom/ConnectionServiceWrapper.java

在方法createConnection 内部有 mBinder.bind(callback, call);这是一个回调函数,调用完成之后,将会调用callback中的onSuccess方法

这里写图片描述

这里写图片描述

会继续调用createConnection,mServiceInterface是IConnectionService的具体实现在ConnectionService.java的匿名内部类中

frameworks/base/telecomm/java/android/telecom/ConnectionService.java

这里写图片描述

发送创建连接消息MSG_CREATE_CONNECTION,之后调用ConnectionService.java中的方法createConnection
这里写图片描述
这里写图片描述

调用onCreateOutgoingConnection去创建连接,该方法为空,所以实现应该再起子类之中

/packages/services/Telephony/src/com/android/services/telephony/TelephonyConnectionService.java
public Connection onCreateOutgoingConnection( PhoneAccountHandle connectionManagerPhoneAccount, final ConnectionRequest request)方法来呼出连接

这里写图片描述

创建TelephonyConnection对象,获取该对象后,调用placeOutgoingConnection
这里写图片描述
这里写图片描述

frameworks/opt/telephony/src/java/com/android/internal/telephony/GsmCdmaPhone.java

发现是调用phone.dial拨打电话,该phone其实是GsCdmaPhone.java对象
public Connection dial(String dialString, UUSInfo uusInfo, int videoState, Bundle intentExtras)

这里写图片描述

dial方法之中还有一个ImsPhone对象,该对象主要处理Volte通话的

protected Connection dialInternal(String dialString, UUSInfo uusInfo, int videoState, Bundle intentExtras, ResultReceiver wrappedCallback)
这里写图片描述
在这里将会调用mCT.dial方法,也就是GsmCdmaCallTracker对象

/frameworks/opt/telephony/src/java/com/android/internal/telephony/GsmCdmaCallTracker.java
这里写图片描述

调用mCi.dial拨打,mCi是RIL对象,在GsmCdmaCallTracker的父类CallTracker中定义,updatePhoneState状态,发起状态改变通知.
那么这些对象是什么时候创建的呢?GsmCdmaCallTracker,RIL对象

/packages/services/Telephony/src/com/android/phone/PhoneApp.java
/packages/services/Telephony/src/com/android/phone/PhoneGlobals.java
/frameworks/opt/telephony/src/java/com/android/internal/telephony/PhoneFactory.java

/packages/services/Telephony/src/com/android/phone/PhoneApp.java
application标签中的android:persistent=”true”表示该应用始终常驻内存,一开始系统服务启动完毕之后,自动加载到内存中
这里写图片描述

这里写图片描述

/packages/services/Telephony/src/com/android/phone/PhoneGlobals.java

这里写图片描述

/frameworks/opt/telephony/src/java/com/android/internal/telephony/PhoneFactory.java


public static void makeDefaultPhone(Context context){
TelephonyComponentFactory telephonyComponentFactory = TelephonyComponentFactory.getInstance();
sCommandsInterfaces[i] = telephonyComponentFactory.makeRIL(context,networkModes[i], cdmaSubscription, i);
这里写图片描述

在makePhone里面创建GsmCdmaPhone,在GsmCdmaPhone的构造方法中创建来GsmCdmaCallTracker
这里写图片描述

综上所述,也就是说在系统开机之后,这些对象已经随着PhoneApp被创建出来了

frameworks/opt/telephony/src/java/com/android/internal/telephony/RIL.java
这里写图片描述

发送请求:RIL_REQUEST_DIAL

/frameworks/opt/telephony/src/java/com/android/internal/telephony/RadioResponse.java主要处理上层发出请求,底层回馈信息并调用RIL对象的processResponseDone方法,完成一次主动请求,相当之前的processsolicated
/frameworks/opt/telephony/src/java/com/android/internal/telephony/RadioIndication.java主要处理底层上报消息,相当之前的processUnsolicated
/hardware/ril/libril/ril_service.cpp
RIL.java中新采用的radioProxy代理类来调用相关操作,该类调用的相关操作最终都是调用ril_service.cpp 中的方法

在RIL里面由对应的请求,dial,那么在RadioResponse.java中由 *+Response,dialResponse
这里写图片描述

这里写图片描述

这里写图片描述

在responseVoid中调用sendMessageResponse向上层发送消息
先去调用mRil.processResponse处理上报,然后发送消息,最终在调用processResponseDone来完成一次对应的请求
发送的消息为EVENT_OPERATION_COMPLETE,该消息是GsmCdmaCallTracker调用dial时利用obtainCompleteMessage去处理的.

这里写图片描述

这里写图片描述

这里写图片描述

在operationComplete中继续调用mCi.getCurrentCalls来获取call状态,消息为EVENT_POLL_CALLS_RESULT

   protected synchronized void handlePollCalls(AsyncResult ar) {
   >>>>>>>>>>>>>>>>>>
             updatePhoneState();  //更新手机状态
             mPhone.notifyPreciseCallStateChanged();//发起状态改变通知
  >>>>>>>>>>>>>>>>>>>
 }

之后底层会自动上报消息:RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED
先调用RIL对象中的processIndication
这里写图片描述

最后mCallStateRegistrants发起通知,GsmCdmaCallTracker注册来该事件, mCi.registerForCallStateChanged(this, EVENT_CALL_STATE_CHANGE, null);
这里写图片描述

GsmCdmaCallTracker处理消息 EVENT_CALL_STATE_CHANGE,最后调用其父类中pollCallsWhenSafe()方法来处理,调用 handlePollCalls来查询call的状态等操作。

拨打电话呼出流程图:

这里写图片描述

作者:u013766436 发表于2017/11/9 19:46:42 原文链接
阅读:199 评论:0 查看评论

自定义View之仿Emui开关控件

$
0
0

1. 简介

使用华为手机,发现设置应用中的开关空间颜色听挺好看,所以自己想写一个类似的,当然不可能完全相同,但是大致的效果差不多。
操作步骤:
1. 截图。PS取色;
2. 判断左右两边的半径;
3. 继承View复写onDraw方法,手动绘制。
4. 控制开关的状态:on/off

2. 效果

这里写图片描述

3. 实现

3.1 自定义属性

没有定义,颜色和半径都是按照自己的效果写成固定的,当然也可以写成自定义属性。

3.2 自定义View代码

涉及到一些动画的使用和View的事件处理

public class CustomSwitchView extends View {
    /**
     * 开关圆点颜色
     */
    private final int SWITCH_DOT_COLOR = 0xffffffff;

    /**
     * 关闭状态下的背景颜色
     */
    private final int OFF_BACKGROUND_COLOR = 0xffe2e2e2;

    /**
     * 打开状态下的背景颜色
     */
    private final int ON_BACKGROUND_COLOR = 0xff007dff;

    /**
     * 边界和开关圆点的间距
     */
    private final int BOUND_DOT_GAP = 8;

    /**
     * 是否打开
     */
    private boolean isOn = false;

    /**
     * 圆点的半径和坐标位置
     */
    private int mRadius;

    private int startX;
    private int endX;
    private float centerX;
    private float centerY;
    private RectF mRectF;

    /**
     * 开关的画笔
     */
    private Paint mSwitchPaint;

    public CustomSwitchView(Context context) {
        this(context, null);
    }

    public CustomSwitchView(Context context,
            @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public CustomSwitchView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    /**
     * 打开或者关闭
     *
     * @param on true 打开, false 关闭
     */
    public void setOn(boolean on) {
        isOn = on;
        ValueAnimator animator;
        if (on) {
            animator = ValueAnimator.ofFloat(centerX, endX);
        } else {
            animator = ValueAnimator.ofFloat(centerX, startX);
        }
        animator.setDuration(200);
        animator.setRepeatCount(0);
        animator.setInterpolator(new LinearInterpolator());
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                centerX = (Float) animation.getAnimatedValue();
                invalidate();
            }
        });
        animator.start();
    }

    private void init() {
        mSwitchPaint = new Paint();
        mSwitchPaint.setColor(OFF_BACKGROUND_COLOR);
        mSwitchPaint.setStrokeWidth(10f);
        mSwitchPaint.setStrokeCap(Paint.Cap.ROUND);
        mSwitchPaint.setAntiAlias(true);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mRectF = new RectF(getLeft(), getTop(), getRight(), getBottom());
        mRadius = h / 2 - BOUND_DOT_GAP;
        startX = getLeft() + mRadius + BOUND_DOT_GAP / 2;
        endX = getRight() - mRadius - BOUND_DOT_GAP / 2;
        centerX = startX;
        centerY = (getTop() + getBottom()) / 2.0f;
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (isOn) {
            mSwitchPaint.setColor(ON_BACKGROUND_COLOR);
        } else {
            mSwitchPaint.setColor(OFF_BACKGROUND_COLOR);
        }
        canvas.drawRoundRect(mRectF, mRadius, mRadius, mSwitchPaint);
        mSwitchPaint.setColor(SWITCH_DOT_COLOR);
        canvas.drawCircle(centerX, centerY, mRadius, mSwitchPaint);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        int action = event.getAction();
        switch (action) {
            case MotionEvent.ACTION_DOWN:
                break;
            case MotionEvent.ACTION_UP:
                setOn(!isOn);
                break;
            case MotionEvent.ACTION_MOVE: {
                float x = event.getX();
                if (x < endX && x > startX) {
                    centerX = x;
                    invalidate();
                }
            }
            break;
            default:
                break;
        }
        return true;
    }
}

4. 源码

源码依然是上传到Github
CustomViewDemo

作者:poorkick 发表于2017/11/9 20:29:23 原文链接
阅读:165 评论:0 查看评论

leetcode题解-13. Roman to Integer && 14. Longest Common Prefix && 20. Valid Parentheses

$
0
0

本次的三道题目都没有什么难度,我们先看第一道,题目如下:

Given a roman numeral, convert it to an integer.

Input is guaranteed to be within the range from 1 to 3999.

就是将古罗马数字转化为int型,上网查一下古罗马数字的表示方法就可以知道,总共有“IVXLCDM”7种字符分别表示1、5、10、50、100、500、1000。以下摘自于维基百科:

重复数次:一个罗马数字重复几次,就表示这个数的几倍。

右加左减:
    在较大的罗马数字的右边记上较小的罗马数字,表示大数字加小数字。
    在较大的罗马数字的左边记上较小的罗马数字,表示大数字减小数字。
    左减的数字有限制,仅限于I、X、C。比如45不可以写成VL,只能是XLV
    但是,左减时不可跨越一个位值。比如,99不可以用IC( {\displaystyle 100-1} 100-1)表示,而是用XCIX( {\displaystyle [100-10]+[10-1]} [100-10]+[10-1])表示。(等同于阿拉伯数字每位数字分别表示。)
    左减数字必须为一位,比如8写成VIII,而非IIX。
    右加数字不可连续超过三位,比如14写成XIV,而非XIIII。(见下方“数码限制”一项。)

所以就很容易得到下面的程序:

    public static int charToInt(char c) {
        int data = 0;

        switch (c) {
            case 'I':
                data = 1;
                break;
            case 'V':
                data = 5;
                break;
            case 'X':
                data = 10;
                break;
            case 'L':
                data = 50;
                break;
            case 'C':
                data = 100;
                break;
            case 'D':
                data = 500;
                break;
            case 'M':
                data = 1000;
                break;
        }

        return data;
    }

    public static int romanToInt(String s) {
        int i, total, pre, cur;

        total = charToInt(s.charAt(0));

        for (i = 1; i < s.length(); i++) {
            pre = charToInt(s.charAt(i - 1));
            cur = charToInt(s.charAt(i));
            //左减右加
            if (cur <= pre) {
                total += cur;
            } else {
                total = total - pre * 2 + cur;
            }
        }

        return total;
    }

再来看第二道题目:

Write a function to find the longest common prefix string amongst an array of strings.

本题是寻找一个字符串数组中所有字符串开头部分的公共子串,也就是找大家都相同的最长子串。逐个遍历然后比较即可得到min子串,代码如下所示:

    public static String longestCommonPrefix(String[] strs) {
        if(strs == null || strs.length == 0)
            return "";
        String min = strs[0];
        for(int i=1; i<strs.length; i++){
            //获得两个字符串中最小的长度
            int len = Math.min(min.length(), strs[i].length());
            //如果被比较的字符串比较短,则将min直接截断至其长度
            min = min.substring(0, len);
            //判断两个字符串是否相等,不等就将min截断在该位置处
            for(int j=0; j<len; j++){
                if(min.charAt(j) != strs[i].charAt(j)) {
                    min = min.substring(0, j);
                    break;
                }
            }

        }
        return min;
    }

这种方法效率比较低,我们可以参考下面的代码来进行改进效率,主要利用了内置的startsWith函数:

    public String longestCommonPrefix1(String[] strs) {
        int n=strs.length;
        if(n==0) return "";
        StringBuilder st=new StringBuilder(strs[0]);
        for(int i=1;i<n;i++){
            //对于要比较多字符串,如果不满足则将st最后一个字符删除,直到找到公共子串。
            while(!strs[i].startsWith(st.toString())) st.deleteCharAt(st.length()-1);
        }
        return st.toString();
    }

此外还有一种方法是,将数组排序,然后直接比较头尾两个字符串即可,这里我们需要理解一下对字符串数组进行排序的依据就是两个字符串的大小判断,就是按照其字符串中字符的顺序进行的:

    public String longestCommonPrefix2(String[] strs) {
        StringBuilder result = new StringBuilder();

        if (strs != null && strs.length > 0) {
            //排序
            Arrays.sort(strs);
            //首位两个字符串
            char[] a = strs[0].toCharArray();
            char[] b = strs[strs.length - 1].toCharArray();
            //比较两个字符串
            for (int i = 0; i < a.length; i++) {
                if (b.length > i && b[i] == a[i]) {
                    result.append(b[i]);
                } else {
                    return result.toString();
                }
            }
            return result.toString();
        } else
            return "";
    }

接下来我们开最后一道题目:

Given a string containing just the characters '(', ')', '{', '}', '[' and ']', determine if the input string is valid.

The brackets must close in the correct order, "()" and "()[]{}" are all valid but "(]" and "([)]" are not.

就是要判断“(){}[]”的开闭顺序是否正确。很容易我们就会想到使用栈stack来解决这个问题,如果有没有正常结束的开,就把相应的符号入栈,然后在出栈。就可以实现本功能,代码入下:

    //50%
    public boolean isValid(String s) {
        Stack<Character> stack = new Stack<Character>();
        for (char c : s.toCharArray()) {
            if (c == '(')
                stack.push(')');
            else if (c == '{')
                stack.push('}');
            else if (c == '[')
                stack.push(']');
            else if (stack.isEmpty() || stack.pop() != c)
                return false;
        }
        return stack.isEmpty();
    }

上面这种方法虽然简单,但是使用了stack数据结构,效率比较低,我们可以使用数组来代替他的功能,代码如下所示:

    //99.9%
    public boolean isValid1(String s) {
        char[] stack = new char[s.length()];
        int head = 0;
        for(char c : s.toCharArray()) {
            switch(c) {
            //下面三种情况,则将其存到数组中
                case '{':
                case '[':
                case '(':
                    stack[head++] = c;
                    break;
                //遇到后三种符号时,则判断数组最后一个元素是否为与之对应的,或者数组为空时,也表明关闭顺序不对
                case '}':
                    if(head == 0 || stack[--head] != '{') return false;
                    break;
                case ')':
                    if(head == 0 || stack[--head] != '(') return false;
                    break;
                case ']':
                    if(head == 0 || stack[--head] != '[') return false;
                    break;
            }
        }
        return head == 0;

    }
作者:liuchonge 发表于2017/11/9 20:56:33 原文链接
阅读:137 评论:0 查看评论

LeetCode-667:Beautiful Arrangement II (数组的完美安排) -- medium

$
0
0

Question

Given two integers n and k, you need to construct a list which contains n different positive integers ranging from 1 to n and obeys the following requirement:
Suppose this list is [a1, a2, a3, … , an], then the list [|a1 - a2|, |a2 - a3|, |a3 - a4|, … , |an-1 - an|] has exactly k distinct integers.

If there are multiple answers, print any of them.

Example 1:

Input: n = 3, k = 1
Output: [1, 2, 3]

Explanation: The [1, 2, 3] has three different positive integers ranging from 1 to 3, and the [1, 1] has exactly 1 distinct integer: 1.

Example 2:

Input: n = 3, k = 2
Output: [1, 3, 2]

Explanation: The [1, 3, 2] has three different positive integers ranging from 1 to 3, and the [2, 1] has exactly 2 distinct integers: 1 and 2.

Note:

  • The n and k are in the range 1 <= k < n <= 104.

问题解析:

给定整数n和k,构造一个包含1-n的n个整数的数组[a1, a2, a3, … , an],使得[|a1 - a2|, |a2 - a3|, |a3 - a4|, … , |an-1 - an|] 新数组含有k个不同的数。

Answer

Solution 1:

二分法。

  • 由题我们可知:1..n最多可以构造出n-1个不同的差,比如 1..9为:[1 9 2 8 3 7 4 6 5]
  • 其相邻元素差的绝对值为:diff: 8 7 6 5 4 3 2 1
  • 观察构造的数据,其是大小交替的。那么这样的话,我们只要先构造出前k个,后面按照顺序来产生1就可以了。
  • 在后面顺序添加的时候注意,需要添加的是增序还是逆序。
  • 如:以1..7为例:
  • k=6:1 7 2 6 3 5 | 4
  • k=5:1 7 2 6 3 | 4 5
  • k=4:1 7 2 6 | 5 4 3
  • k=3:1 7 2 | 3 4 5 6
  • k=2:1 7 | 6 5 4 3 2
  • k=1:1 | 2 3 4 5 6 7
class Solution {
    public int[] constructArray(int n, int k) {
        int[] ans = new int[n];
        int l = 1, r = n;
        int i = 0;
        for (; i < k; i++){
            if (i % 2 == 0) ans[i] = l++;
            else ans[i] = r--;
        }

        if(i % 2 == 1){
            for (int j = l; j <= r; j++) ans[i++] = j;
        }else{
            for (int j = r; j >= l; j--) ans[i++] = j;
        }

        return ans;
    }
}
  • 时间复杂度:O(n),空间复杂度:O(n)
作者:Koala_Tree 发表于2017/11/9 21:02:55 原文链接
阅读:129 评论:0 查看评论

Angular 4入门教程系列:8:Tour Of Heroes之前后端服务

$
0
0

这里写图片描述
这篇文章我们将会重点学习一下Angular的HttpModule和In-Memory Web API的使用方法。

学习时间

大概5-10分钟。

事前准备

需要事前安装模块angular-in-memory-web-api才能保证此部分学习能够正常进行。因为这个系列中我们使用的是node的官方镜像,这个过程中我们使用了yarn进行处理包的依赖,所以接下来的部分我们将继续使用yarn进行安装。

方式1:

yarn add angular-in-memory-web-api
这样将会自动进行安装并把信息保存到package.json中

/workspace/HelloAngular # yarn add angular-in-memory-web-api
yarn add v1.2.0
[1/4] Resolving packages...
[2/4] Fetching packages...
info fsevents@1.1.2: The platform "linux" is incompatible with this module.
info "fsevents@1.1.2" is an optional dependency and failed compatibility check. Excluding it from installation.
[3/4] Linking dependencies...
[4/4] Building fresh packages...
success Saved 1 new dependency.
└─ angular-in-memory-web-api@0.3.1
Done in 51.71s.
/workspace/HelloAngular #

方式2:

修改package.json,然后使用yarn install

方式3:

使用npm install方式的开发者可以使用npm install angular-in-memory-web-api,并根据情况决定是否-g安装

注意:使用官方教程的时候angular-in-memory-web-api的最新版本0.5.1似乎有问题,使用0.3.1没有任何问题。没有细究具体原因。不然有可能因为其无法正常动作导致数据取不到,最终页面提示没有slice属性,其原因是因为没有取到数据而已。

InMemoryDataService

到目前为止,我们使用的是一个Hero的全局数组来模拟数据,接下来我们使用InMemoryDbService来进行模拟,所做的内容也非常类似,我们在createDb中创建一个数组,而这些数组保存的普通Json数据的格式,而非直接的对象。

/workspace/HelloAngular/src/app # cat in-memory-data.service.ts
import { InMemoryDbService } from 'angular-in-memory-web-api';
export class InMemoryDataService implements InMemoryDbService {
  createDb() {
    const heroes = [
      { id: 0,  name: 'Zero' },
      { id: 11, name: 'Mr. Nice' },
      { id: 12, name: 'Narco' },
      { id: 13, name: 'Bombasto' },
      { id: 14, name: 'Celeritas' },
      { id: 15, name: 'Magneta' },
      { id: 16, name: 'RubberMan' },
      { id: 17, name: 'Dynama' },
      { id: 18, name: 'Dr IQ' },
      { id: 19, name: 'Magma' },
      { id: 20, name: 'Tornado' }
    ];
    return {heroes};
  }
}
/workspace/HelloAngular/src/app #

使用方法:
注意此处的使用方式,在getHeroes中使用http模块的功能,虽然是模拟,但是跟实际的前后端开发,通过接口取到后端提供的json数据的实际方式,同前面的例子相比已经发生了天渊之别。

/workspace/HelloAngular/src/app # cat hero.service.ts
import { Injectable } from '@angular/core';
import { Http       } from '@angular/http';

import 'rxjs/add/operator/toPromise';
import { Hero } from './hero';

@Injectable()
export class HeroService {
  private heroesUrl = 'api/heroes';

  constructor(private http: Http) {}

  getHeroes(): Promise<Hero[]> {
    return this.http.get(this.heroesUrl)
             .toPromise()
             .then(response => response.json().data as Hero[])
             .catch(this.handleError);
  }

  private handleError(error: any): Promise<any> {
    console.error('An error occurred', error); // for demo purposes only
    return Promise.reject(error.message || error);
  }

  getHero(id: number): Promise<Hero> {
    return this.getHeroes()
             .then(heroes => heroes.find(hero => hero.id === id));
  }
}
/workspace/HelloAngular/src/app # 

引入根模块

/workspace/HelloAngular/src/app # cat app.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { HttpModule }  from '@angular/http';
import { InMemoryWebApiModule } from 'angular-in-memory-web-api';
import { InMemoryDataService }  from './in-memory-data.service';

import { AppComponent } from './app.component';
import { HeroDetailComponent } from './hero-detail.component'
import { HeroService } from './hero.service';
import { HeroesComponent } from './heroes.component';
import { DashboardComponent } from './dashboard.component';
import { AppRoutingModule } from './app-routing.module';


@NgModule({
  declarations: [
    AppComponent,
    HeroDetailComponent,
    HeroesComponent,
    DashboardComponent
  ],
  imports: [
    BrowserModule,
    FormsModule,
    HttpModule,
    InMemoryWebApiModule.forRoot(InMemoryDataService),
    AppRoutingModule
  ],
  providers: [HeroService],
  bootstrap: [AppComponent]
})

export class AppModule { }
/workspace/HelloAngular/src/app # 

Http get

实际上我们使用HTTP的get来取到并显示信息,具体页面信息如下:
这里写图片描述

Http put

现在的页面修改了信息之后,如果按back的按钮则不能像像之前那样能够得到保存,因为之前保存在全局数组里面,自然可以。而是用http的put方法则可以实现保存的功能,简单来说,需要做如下几件事情:

  • * 在hero-detail的模板中添加一个保存的按钮 *
  • * 在添加的按钮中调用 hero的service的update方法 *
  • * 在update方法中使用http模块的put进行信息的保存 *

hero-detail.component.ts

/workspace/HelloAngular/src/app # cat hero-detail.component.ts
import { Component, Input } from '@angular/core';
import { OnInit } from '@angular/core';
import { ActivatedRoute, ParamMap } from '@angular/router';
import { Location } from '@angular/common';

import 'rxjs/add/operator/switchMap';

import { Hero } from './hero';
import { HeroService } from './hero.service';
@Component({
  selector: 'hero-detail',
  template: `
    <div *ngIf="hero">
      <h2>{{hero.name}} details!</h2>
      <div><label>id: </label>{{hero.id}}</div>
      <div>
        <label>name: </label>
        <input [(ngModel)]="hero.name" placeholder="name"/>
      </div>
      <button (click)="goBack()">Back</button>
      <button (click)="save()">Save</button>
    </div>
  `
})
export class HeroDetailComponent implements OnInit {
  @Input() hero: Hero;

  constructor(
    private heroService: HeroService,
    private route: ActivatedRoute,
    private location: Location
  ) {
  }

  ngOnInit(): void {
    this.route.paramMap
      .switchMap((params: ParamMap) => this.heroService.getHero(+params.get('id')))
      .subscribe(hero => this.hero = hero);
  }

  goBack(): void {
    this.location.back();
  }

  save(): void {
    this.heroService.update(this.hero)
      .then(() => this.goBack());
  }
}
/workspace/HelloAngular/src/app #

hero.service.ts

/workspace/HelloAngular/src/app # cat hero.service.ts
import { Injectable } from '@angular/core';
import { Http       } from '@angular/http';
import { Headers    } from '@angular/http';

import 'rxjs/add/operator/toPromise';
import { Hero } from './hero';

@Injectable()
export class HeroService {
  private heroesUrl = 'api/heroes';
  private headers = new Headers({'Content-Type': 'application/json'});

  constructor(private http: Http) {}

  getHeroes(): Promise<Hero[]> {
    return this.http.get(this.heroesUrl)
             .toPromise()
             .then(response => response.json().data as Hero[])
             .catch(this.handleError);
  }

  private handleError(error: any): Promise<any> {
    console.error('An error occurred', error); // for demo purposes only
    return Promise.reject(error.message || error);
  }

  getHero(id: number): Promise<Hero> {
    return this.getHeroes()
             .then(heroes => heroes.find(hero => hero.id === id));
  }

  update(hero: Hero): Promise<Hero> {
    const url = `${this.heroesUrl}/${hero.id}`;
    return this.http
      .put(url, JSON.stringify(hero), {headers: this.headers})
      .toPromise()
      .then(() => hero)
      .catch(this.handleError);
  }
}
/workspace/HelloAngular/src/app #

结果确认

修改英雄信息:
这里写图片描述
点击save按钮后,同样是goBack,但是信息被保存了下来
这里写图片描述

Http post

使用http post以便进行添加Hero,需要做如下几件事情:

  • * 在hero模板中添加用于添加hero的按钮 *
  • * 在添加的按钮中调用 hero的service的create方法 *
  • * 在create方法中使用http模块的post进行信息的添加 *

heroes.component.html

/workspace/HelloAngular/src/app # cat heroes.component.html
  <h1>{{title}}</h1>
  <div>
    <label>Hero name:</label> <input #heroName />
    <button (click)="add(heroName.value); heroName.value=''">
      Add
    </button>
  </div>

  <h2>My Heroes</h2>
  <ul class="heroes">
    <li *ngFor="let hero of heroes"  [class.selected]="hero === selectedHero" (click)="onSelect(hero)">
       <span class="badge">{{hero.id}}</span> {{hero.name}}
    </li> 
  </ul>

  <div *ngIf="selectedHero">
    <h2>
      {{selectedHero.name | uppercase}} is my hero
    </h2>
    <button (click)="gotoDetail()">View Details</button>
  </div>


/workspace/HelloAngular/src/app #

heroes.component.ts

/workspace/HelloAngular/src/app # cat heroes.component.ts
import { Component } from '@angular/core';
import { OnInit    } from '@angular/core';
import { Router    } from '@angular/router';

import { Hero } from './hero';
import { HeroService } from './hero.service';

@Component({
  selector: 'my-heroes',
  templateUrl: './heroes.component.html',
  styleUrls: ['./heroes.component.css'],
  providers: []
})
export class HeroesComponent implements OnInit {
  title = 'Tour of Heroes';
  selectedHero: Hero;
  heroes: Hero[];

  onSelect(hero: Hero): void {
    this.selectedHero = hero;
  }

  ngOnInit(): void{
    this.heroService.getHeroes().then(heroes => this.heroes = heroes); 
  }

  constructor(
    private router: Router,
    private heroService: HeroService) {
  }

  gotoDetail(): void {
    this.router.navigate(['/detail', this.selectedHero.id]);
  }

  add(name: string): void {
    name = name.trim();
    if (!name) { return; }
    this.heroService.create(name)
      .then(hero => {
        this.heroes.push(hero);
        this.selectedHero = null;
      });
  }
}
/workspace/HelloAngular/src/app #

hero.service.ts

/workspace/HelloAngular/src/app # cat hero.service.ts
import { Injectable } from '@angular/core';
import { Http       } from '@angular/http';
import { Headers    } from '@angular/http';

import 'rxjs/add/operator/toPromise';
import { Hero } from './hero';

@Injectable()
export class HeroService {
  private heroesUrl = 'api/heroes';
  private headers = new Headers({'Content-Type': 'application/json'});

  constructor(private http: Http) {}

  getHeroes(): Promise<Hero[]> {
    return this.http.get(this.heroesUrl)
             .toPromise()
             .then(response => response.json().data as Hero[])
             .catch(this.handleError);
  }

  private handleError(error: any): Promise<any> {
    console.error('An error occurred', error); // for demo purposes only
    return Promise.reject(error.message || error);
  }

  getHero(id: number): Promise<Hero> {
    return this.getHeroes()
             .then(heroes => heroes.find(hero => hero.id === id));
  }

  update(hero: Hero): Promise<Hero> {
    const url = `${this.heroesUrl}/${hero.id}`;
    return this.http
      .put(url, JSON.stringify(hero), {headers: this.headers})
      .toPromise()
      .then(() => hero)
      .catch(this.handleError);
  }

  create(name: string): Promise<Hero> {
    return this.http
      .post(this.heroesUrl, JSON.stringify({name: name}), {headers: this.headers})
      .toPromise()
      .then(res => res.json().data as Hero)
      .catch(this.handleError);
  }
}
/workspace/HelloAngular/src/app # 

结果确认

用新加的add按钮和输入框添加两个英雄New Hero1和New Hero2
这里写图片描述
可以确认已经实时的添加到列表中了
这里写图片描述

Http delete

使用http delete以便进行删除Hero,需要做如下几件事情:

  • * 在heroes模板中添加用于删除hero的按钮 *
  • * 在添加的按钮中调用 hero的service的delete方法 *
  • * 在delete方法中使用http模块的delete进行信息的删除*

heroes.component.html

/workspace/HelloAngular/src/app # cat heroes.component.html
  <h1>{{title}}</h1>
  <div>
    <label>Hero name:</label> <input #heroName />
    <button (click)="add(heroName.value); heroName.value=''">
      Add
    </button>
  </div>

  <h2>My Heroes</h2>
  <ul class="heroes">
    <li *ngFor="let hero of heroes"  [class.selected]="hero === selectedHero" (click)="onSelect(hero)">
       <span class="badge">{{hero.id}}</span> {{hero.name}}
       <button class="delete"
               (click)="delete(hero); $event.stopPropagation()">x</button>
    </li> 
  </ul>

  <div *ngIf="selectedHero">
    <h2>
      {{selectedHero.name | uppercase}} is my hero
    </h2>
    <button (click)="gotoDetail()">View Details</button>
  </div>


/workspace/HelloAngular/src/app #

heroes.component.ts

/workspace/HelloAngular/src/app # cat heroes.component.ts
import { Component } from '@angular/core';
import { OnInit    } from '@angular/core';
import { Router    } from '@angular/router';

import { Hero } from './hero';
import { HeroService } from './hero.service';

@Component({
  selector: 'my-heroes',
  templateUrl: './heroes.component.html',
  styleUrls: ['./heroes.component.css'],
  providers: []
})
export class HeroesComponent implements OnInit {
  title = 'Tour of Heroes';
  selectedHero: Hero;
  heroes: Hero[];

  onSelect(hero: Hero): void {
    this.selectedHero = hero;
  }

  ngOnInit(): void{
    this.heroService.getHeroes().then(heroes => this.heroes = heroes); 
  }

  constructor(
    private router: Router,
    private heroService: HeroService) {
  }

  gotoDetail(): void {
    this.router.navigate(['/detail', this.selectedHero.id]);
  }

  add(name: string): void {
    name = name.trim();
    if (!name) { return; }
    this.heroService.create(name)
      .then(hero => {
        this.heroes.push(hero);
        this.selectedHero = null;
      });
  }

  delete(hero: Hero): void {
    this.heroService
        .delete(hero.id)
        .then(() => {
          this.heroes = this.heroes.filter(h => h !== hero);
          if (this.selectedHero === hero) { this.selectedHero = null; }
        });
  }
}
/workspace/HelloAngular/src/app #

hero.service.ts

/workspace/HelloAngular/src/app # cat hero.service.ts
import { Injectable } from '@angular/core';
import { Http       } from '@angular/http';
import { Headers    } from '@angular/http';

import 'rxjs/add/operator/toPromise';
import { Hero } from './hero';

@Injectable()
export class HeroService {
  private heroesUrl = 'api/heroes';
  private headers = new Headers({'Content-Type': 'application/json'});

  constructor(private http: Http) {}

  getHeroes(): Promise<Hero[]> {
    return this.http.get(this.heroesUrl)
             .toPromise()
             .then(response => response.json().data as Hero[])
             .catch(this.handleError);
  }

  private handleError(error: any): Promise<any> {
    console.error('An error occurred', error); // for demo purposes only
    return Promise.reject(error.message || error);
  }

  getHero(id: number): Promise<Hero> {
    return this.getHeroes()
             .then(heroes => heroes.find(hero => hero.id === id));
  }

  update(hero: Hero): Promise<Hero> {
    const url = `${this.heroesUrl}/${hero.id}`;
    return this.http
      .put(url, JSON.stringify(hero), {headers: this.headers})
      .toPromise()
      .then(() => hero)
      .catch(this.handleError);
  }

  create(name: string): Promise<Hero> {
    return this.http
      .post(this.heroesUrl, JSON.stringify({name: name}), {headers: this.headers})
      .toPromise()
      .then(res => res.json().data as Hero)
      .catch(this.handleError);
  }

  delete(id: number): Promise<void> {
    const url = `${this.heroesUrl}/${id}`;
    return this.http.delete(url, {headers: this.headers})
      .toPromise()
      .then(() => null)
      .catch(this.handleError);
  }
}
/workspace/HelloAngular/src/app #

结果确认

Hero列表的显示页面中,每个英雄都有一个可删除的按钮
这里写图片描述
删除直到只剩4个
这里写图片描述
现在按钮对的不齐,修改CSS让它们对准一点,添加如下代码到heroes.component.css中

button.delete {
  float:right;
  margin-top: 2px;
  margin-right: .8em;
  background-color: gray !important;
  color:white;
}

结果最终显示为:
这里写图片描述

总结

通过学习使用angular-in-memory-web-api,可以学习到如何做一个模拟的后端,在实际的项目中完全可以模拟后端无法进行联调测试的情况,具有很好的实际意义。

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

从零开始前端学习[47]:如何使用javascript来操作行内样式或者属性浅析

$
0
0

如何使用javascript来操作行内样式或者属性

  • 使用javascript来操作行内样式,或者标签固有属性

提示
博主:章飞_906285288
博客地址:http://blog.csdn.net/qq_29924041


使用javascript来操作行内样式,或者标签固有属性

我们知道,html是结构层,css是样式层,而js则是行为层,一个页面的样式控制主要是css来进行控制的,而在css中样式的引入又包括了外部引用,内部引用,或者行内样式等,但是这些样式都是通过样式属性来进行的。
内部样式或者行内样式都是写在style标签内的,而一般对于js所操纵的样式,一般情况下暂且可以理解为行内样式,那什么是行内样式呢???行内样式是写在左标签内部的样式,通过style来引入,

如下所示:
  <p class="fl_l" id="innerHTML_2" style="font-size: 20px;color: red;background: blue;text-align: center;">innerHTML_2</p>

并且行内样式也是最高的。

那怎么进行操作的呢?如下所示语法:

    语法:对象.style.css属性 = ""; 如果有“-”就要用驼峰命名(把减号去掉,减号后面的第一个字母大写,如backgroundColor)
    如:
    var box = document.getElementById("box");
    box.onclick = function(){
        box.style.backgroundColor = '#999';
    }

注意在js中引用的时候是不存在有中间的”-“的,同样的,其属性值与原来基本上也是一致的,并没有太大区别。

注意其在引用的时候是需要先引入style的。

关于通过js浮动的一点简单介绍:
在我们的前端里面我们都知道兼容很恼火,这里的浮动就有一个兼容问题,首先,在js里面我们不能直接这样子写

oBox.style.flaot = "right" 错误的写法

因为flaot是关键字,虽然在谷歌里面这样写也可以这样子写,但是,我们也不能这样子写,比如c++里面是代表浮点型,所以我们用其他的代替,还有我们的class也是保留字要用className代替

所以在google里面的话:

oBox.style.cssFloat = 'right';

而在低版本的浏览器中的 话:

oBox.style.styleFloat = 'right';

最重要的还有父级元素坍塌问题的思考,因为在你设置了浮动之后,如果不去清除浮动,势必会造成父级元素的坍塌的现象,这个时候你必须要清除浮动,

parent.setAttribute("class",'clearfix');  /*设置类属性来消除掉父级坍塌事件**/

具体会通过代码的形式来展示:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
  <meta charset="UTF-8"><!--申明当前网页的编码集UTF-8-->
  <meta name="Generator" content="EditPlus®">   <!--编辑器的名称-->
  <meta name="Author" content="作者是谁">       
  <meta name="Keywords" content="关键词">
  <meta name="Description" content="描述和简介">
  <style type="text/css">                                        
        body,dl,dd,dt,p,h1,h2,h3,h4,h5,h6{ margin: 0;}
        ul,ol{margin: 0; list-style: none; padding: 0;}
        a{ text-decoration: none; }
        *{ margin: 0; padding: 0; }
        .main{width: 800px;margin: 40px auto;box-shadow: 0 0 10px 0 deeppink}
        p{width: 200px;height: 200px;box-shadow: 0 0 10px 0 blue;margin: 10px}
        .clearfix:after{
          display: block;
          content:"";
          clear: both;
        }
  </style>
</head>
<body>
<div class="main">
  <div class="compare1" id="parent">
    <p  id="innerHTML_1">1</p>
    <p  id="innerHTML_2">2</p>
  </div>
</div>
<script>
  var parent = document.getElementById("parent");
  var p1 = document.getElementById("innerHTML_1");
  var p2 = document.getElementById("innerHTML_2");

  p1.onmouseover = function () {
      p1.style.backgroundColor = 'blue';
      p1.style.color = "red";
  }
  p1.onmouseout = function () {
      p1.style.backgroundColor = 'deeppink';
      p1.style.color = "#334499";
  }
  p2.onmouseover = function () {
      p2.style.fontSize = "26px";
      p2.style.textAlign = "center";
  }
  p2.onmouseout = function () {
      p2.style.textAlign = "right";
  }

  parent.onclick= function () {
      parent.style.color = "red";  /*修改字体颜色属性**/
      p1.style.cssFloat = "left";  /*google浏览器让其进行浮动*/
      p1.style.styleFloat = "left"; /*低版本的情况下,浮动*/
      p2.style.cssFloat = "left";
      p2.style.styleFloat = "left";
      parent.setAttribute("class",'clearfix');  /*设置类属性来消除掉父级坍塌事件**/
      console.log("======");
  }
</script>
</body>
</html>

显示如下所示:
这里写图片描述

注意修改行内样式的时候,需要用到.style.cssXXX,浮动相关的样式修改的话,这个时候是有区别的还,再就是浮动的时候需要清除浮动

作者:qq_29924041 发表于2017/11/9 21:28:21 原文链接
阅读:288 评论:0 查看评论

BZOJ3450 Tyvj1952 Easy

$
0
0

标签:期望,数学,递推

Description

某一天WJMZBMR在打osu~~~但是他太弱逼了,有些地方完全靠运气:(
我们来简化一下这个游戏的规则
有n次点击要做,成功了就是o,失败了就是x,分数是按comb计算的,连续a个comb就有a*a分,comb就是极大的连续o。
比如ooxxxxooooxxx,分数就是2*2+4*4=4+16=20。
Sevenkplus闲的慌就看他打了一盘,有些地方跟运气无关要么是o要么是x,有些地方o或者x各有50%的可能性,用?号来表示。
比如oo?xx就是一个可能的输入。
那么WJMZBMR这场osu的期望得分是多少呢?
比如oo?xx的话,?是o的话就是oooxx => 9,是x的话就是ooxxx => 4
期望自然就是(4+9)/2 =6.5了

Input


第一行一个整数n,表示点击的个数
接下来一个字符串,每个字符都是ox?中的一个

Output

一行一个浮点数表示答案
四舍五入到小数点后4位
如果害怕精度跪建议用long double或者extended

Sample Input

4
????

Sample Output

4.1250


n<=300000
osu很好玩的哦
WJMZBMR技术还行(雾),x基本上很少呢

 

分析:

F[i]表示前i项的期望值,g[i]表示当前一段区间内连续一段‘o’的期望长度

然后对于每个字符更新f[i]和g[i]

Code

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define dep(i,a,b) for(int i=a;i>=b;i--)
#define ll long long
#define mem(x,num) memset(x,num,sizeof x)
#ifdef WIN32
#define LL "%I64d"
#else
#define LL "%lld"
#endif
using namespace std;
inline ll read()
{
    ll f=1,x=0;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
const int maxn=3e5+6;
int n;
double g[maxn],f[maxn];
char ch;
int main()
{
	n=read();
	rep(i,1,n){
		ch=getchar();
		if(ch=='o'){g[i]=g[i-1]+1;f[i]=f[i-1]+2*g[i-1]+1;}
		if(ch=='x'){g[i]=0;f[i]=f[i-1];}
		if(ch=='?'){g[i]=g[i-1]/2+0.5;f[i]=f[i-1]+g[i-1]+0.5;}
	}
	printf("%.4lf\n",f[n]);
	return 0;
}
		


作者:qwerty1125 发表于2017/11/9 21:48:09 原文链接
阅读:148 评论:0 查看评论

Dijkstra双栈算术表达式求值算法

$
0
0
package com.vadonmo.exp.example;

import java.util.Stack;

import com.vadonmo.exp.api.StdIn;
import com.vadonmo.exp.api.StdOut;

/**
 * Dijkstra双栈算术表达式求值算法
 * @author vadon
 *
 */
public class Evalua {

    public static void main(String[] args) {
        Stack<String> ops = new Stack<String>();
        Stack<Double> vals = new Stack<Double>();
        while (!StdIn.isEmpty()) {
            String s = StdIn.readString();
            if (s.equals("(")) {

            }else if (s.equals("+")) {
                ops.push(s);
            }else if (s.equals("-")) {
                ops.push(s);
            }else if (s.equals("*")) {
                ops.push(s);
            }else if (s.equals("/")) {
                ops.push(s);
            }else if (s.equals("sqrt")) {
                ops.push(s);
            }else if (s.equals(")")) {
                String op = ops.pop();
                double v = vals.pop();
                if (op.equals("+")) {
                    v = vals.pop() + v;
                }else if (op.equals("-")) {
                    v = vals.pop() - v;
                }else if (op.equals("*")) {
                    v = vals.pop() * v;
                }else if (op.equals("/")) {
                    v = vals.pop() / v;
                }else if (op.equals("sqrt")) {
                    v = Math.sqrt(v);
                }
                vals.push(v);
            }else {
                vals.push(Double.parseDouble(s));
            }
        }
        StdOut.println(vals.pop());
    }

}
作者:vadonmo 发表于2017/11/9 22:33:37 原文链接
阅读:16 评论:0 查看评论

BZOJ4008 [HNOI2015]亚瑟王

$
0
0

标签:数学期望,DP

Description

小 K 不慎被 LL 邪教洗脑了,洗脑程度深到他甚至想要从亚瑟王邪教中脱坑。

他决定,在脱坑之前,最后再来打一盘亚瑟王。既然是最后一战,就一定要打得漂

亮。众所周知,亚瑟王是一个看脸的游戏,技能的发动都是看概率的。作为一个非

洲人,同时作为一个前 OIer,小 K 自然是希望最大化造成伤害的期望值。但他已

经多年没写过代码,连 Spaly都敲不对了,因此,希望你能帮帮小 K,让他感受一

下当欧洲人是怎样的体验。 

本题中我们将考虑游戏的一个简化版模型。 

玩家有一套卡牌,共 n张。游戏时,玩家将 n 张卡牌排列成某种顺序,排列后

将卡牌按从前往后依次编号为 1 ~  n。本题中,顺序已经确定,即为输入的顺序。

每张卡牌都有一个技能。第 i 张卡牌的技能发动概率为 pi,如果成功发动,则会对

敌方造成di点伤害。也只有通过发动技能,卡牌才能对敌方造成伤害。基于现实因

素以及小K非洲血统的考虑,pi不会为 0,也不会为 1,即 0 < pi < 1。 

一局游戏一共有 r 轮。在每一轮中,系统将从第一张卡牌开始,按照顺序依次

考虑每张卡牌。在一轮中,对于依次考虑的每一张卡牌: 

1如果这张卡牌在这一局游戏中已经发动过技能,则 

1.1 如果这张卡牌不是最后一张,则跳过之(考虑下一张卡牌); 

否则(是最后一张),结束这一轮游戏。 

2否则(这张卡牌在这一局游戏中没有发动过技能),设这张卡牌为第 i 张 

2.1将其以 pi的概率发动技能。 

2.2如果技能发动,则对敌方造成 di点伤害,并结束这一轮。 

2.3如果这张卡牌已经是最后一张(即 i 等于n),则结束这一轮;否则,

考虑下一张卡牌。 

请帮助小 K 求出这一套卡牌在一局游戏中能造成的伤害的期望值。 

Input

输入文件的第一行包含一个整数 T,代表测试数据组数。 

接下来一共 T 组数据。 

每组数据的第一行包含两个用空格分开的整数 n和r,分别代表卡牌的张数和

游戏的轮数。 

接下来 n行,每行包含一个实数和一个整数,由空格隔开,描述一张卡牌。第

i 行的两个数为 pi和 di,分别代表第 i 张卡牌技能发动的概率(实数)和技能发动

造成的伤害(整数)。保证 pi最多包含 4位小数,且为一个合法的概率。 

Output

 对于每组数据,输出一行,包含一个实数,为这套卡牌在这一局游戏中造成的

伤害的期望值。对于每一行输出,只有当你的输出和标准答案的相对误差不超过

10^-8时——即|a-o|/a<=10-8时(其中a是标准答案,o是输出),你的输出才会被判为正确。

建议输出10 位小数。 

Sample Input

1

3 2

0.5000 2

0.3000 3

0.9000 1

Sample Output

3.2660250000

HINT

 一共有 13 种可能的情况: 

 

1.  第一轮中,第 1张卡牌发动技能;第二轮中,第 2张卡牌发动技能; 

 

概率为 0.15,伤害为5。 

 

2.  第一轮中,第 1张卡牌发动技能;第二轮中,第 3张卡牌发动技能; 

 

概率为 0.315,伤害为3。 

 

3.  第一轮中,第 1张卡牌发动技能;第二轮不发动技能; 

 

概率为 0.035,伤害为2。 

 

4.  第一轮中,第 2张卡牌发动技能;第二轮中,第 1张卡牌发动技能; 

 

概率为 0.075,伤害为5。 

 

5.  第一轮中,第 2张卡牌发动技能;第二轮中,第 3张卡牌发动技能; 

 

概率为 0.0675,伤害为4。 

 

6.  第一轮中,第 2张卡牌发动技能;第二轮不发动技能; 

 

概率为 0.0075,伤害为3。 

 

7.  第一轮中,第 3张卡牌发动技能;第二轮中,第 1张卡牌发动技能; 

 

概率为 0.1575,伤害为3。 

 

8.  第一轮中,第 3张卡牌发动技能;第二轮中,第 2张卡牌发动技能; 

 

概率为 0.04725,伤害为4。 

 

9.  第一轮中,第 3张卡牌发动技能;第二轮不发动技能; 

 

概率为 0.11025,伤害为1。 

 

10.  第一轮不发动技能;第二轮中,第 1张卡牌发动技能; 

 

概率为 0.0175,伤害为2。 

 

11.  第一轮不发动技能;第二轮中,第 2张卡牌发动技能; 

 

概率为 0.00525,伤害为3。 

 

12.  第一轮不发动技能;第二轮中,第 3张卡牌发动技能; 

 

概率为 0.011025,伤害为1。 

 

13.  第一轮不发动技能;第二轮亦不发动技能; 

 

概率为 0.001225,伤害为0。 

 

造成伤害的期望值为概率与对应伤害乘积之和,为3.266025。 

 

 

对于所有测试数据, 1 <= T <= 444, 1 <= n <= 220, 0<= r <= 132, 0< pi < 1, 0 <= di <= 1000。  

 

除非备注中有特殊说明,数据中 pi与di均为随机生成。 

 

请注意可能存在的实数精度问题,并采取适当措施。 

 

分析:

对于每一张牌,只有前面的牌会影响它的期望,后面的牌对它无影响

所以满足DP的性质

F[i][j]表示前i张牌,用剩j次机会的概率

F[i][j]=f[i-1][j]*(1-p[i])^j+f[i-1][j+1]*(1-(1-p[i])^(j+1))

前面一部分表示第i张牌没有用掉这次机会,后面表示这张牌用掉了这次机会的概率总和

 

Code

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define dep(i,a,b) for(int i=a;i>=b;i--)
#define ll long long
#define mem(x,num) memset(x,num,sizeof x)
#ifdef WIN32
#define LL "%I64d\n"
#else
#define LL "%lld\n"
#endif
using namespace std;
inline ll read()
{
	ll f=1,x=0;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
const int maxn=306;
double p[maxn],pw[maxn][maxn],f[maxn][maxn],ans=0.0;
int a[maxn],T,n,r;

int main()
{
	T=read();
	while(T--){
		n=read(),r=read();
		rep(i,1,n){
			cin>>p[i]>>a[i];
			p[i]=1-p[i];
		}
		rep(i,1,n){
			pw[i][0]=1.0;
			rep(j,1,r)pw[i][j]=pw[i][j-1]*p[i];
		}
		mem(f,0);
		f[0][r]=1.0;ans=0.0;
		rep(i,1,n){
			double s=0.0;
			dep(j,r,0){
			    f[i][j]=f[i-1][j]*pw[i][j]+f[i-1][j+1]*(1-pw[i][j+1]);
			    s+=f[i-1][j+1]*(1-pw[i][j+1]);
			}
			ans+=s*double(a[i]);
		}
		printf("%.10lf\n",ans);
	}
	return 0;
}


作者:qwerty1125 发表于2017/11/10 8:56:55 原文链接
阅读:0 评论:0 查看评论

BZOJ3036 绿豆蛙的归宿

$
0
0

标签:图的遍历,数学期望

Description

随着新版百度空间的下线,Blog宠物绿豆蛙完成了它的使命,去寻找它新的归宿。

给出一个有向无环的连通图,起点为1终点为N,每条边都有一个长度。绿豆蛙从起点出发,走向终点。
到达每一个顶点时,如果有K条离开该点的道路,绿豆蛙可以选择任意一条道路离开该点,并且走向每条路的概率为 1/K 。
现在绿豆蛙想知道,从起点走到终点的所经过的路径总长度期望是多少?

Input

第一行: 两个整数 N M,代表图中有N个点、M条边
第二行到第 1+M 行: 每行3个整数 a b c,代表从a到b有一条长度为c的有向边

Output


从起点到终点路径总长度的期望值,四舍五入保留两位小数。

 

Sample Input

4 4

1 2 1

1 3 2

2 3 3

3 4 4



Sample Output

7.00

HINT



对于100%的数据  N<=100000,M<=2*N

珍惜NOIP前最后的刷水机会2333

Code

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define dep(i,a,b) for(int i=a;i>=b;i--)
#define ll long long
#define mem(x,num) memset(x,num,sizeof x)
#ifdef WIN32
#define LL "%I64d"
#else
#define LL "%lld"
#endif
using namespace std;
inline ll read()
{
    ll f=1,x=0;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
const int maxn=1e5+6;
int n,m,last[maxn],vis[maxn],r[maxn]={0};
struct edge{int to,next,w;}e[maxn<<1];
double f[maxn];
void dfs(int x)
{
    if(!vis[x])vis[x]=1;
    else return;
#define reg(x) for(int i=last[x];i;i=e[i].next)
#define v e[i].to
    reg(x){
        dfs(v);
        f[x]+=e[i].w+f[v];
    }
    if(r[x])f[x]/=r[x];
}
int main()
{
    n=read(),m=read();
    rep(i,1,m){
        int x=read(),y=read(),z=read();
        e[i]=(edge){y,last[x],z};last[x]=i;
        r[x]++;
    }
    dfs(1);
    printf("%.2lf\n",f[1]);
    return 0;
}


作者:qwerty1125 发表于2017/11/10 9:05:56 原文链接
阅读:0 评论:0 查看评论

spring in action 4 第一章《spring之旅》

$
0
0
第一章 《spring之旅》


1、Spring究竟是什么?
   Spring本身就是一个容器,你应用里面的对象都由Spring来管理。我们下载的Spring框架只是常用的是Spring的核心框架而已,而不是Spring里面的全部的内容。
   而Spring包括了很多基于核心框架的框架和一些类库。


2、Spring出现的目的:
   简化java开发,增强了POJO的功能。


3、Spring为达到目的,采取了哪些策略?
   4个关键策略:
   (1)基于POJO的轻量级和最小侵入式编程。
   (2)通过AOP和DI实现松耦合。
   (3)基于切面和惯例进行声明式编程。
   (4)通过切面和模版减少了模版式编程。


4、我们下载的Spring模块都包括了哪些内容?
   我下载的是spring release4.3.8版本,并查看libs文件夹,里面共计60个jar文件。
   这60个文件属于20个不同的模块.每个模块有3个jar文件(包括:javadoc的jar文件,源码的jar文件,二进制类库)。
   这20个模块按照功能划分,可以分为6个功能模块:
   (1)Spring核心容器:这是Spring框架最核心的部分,它管理了Spring应用中bean的创建、配置、管理。
      包括的jar文件:beans,core, context, expression, context-support
   (2)Spring的AOP模块:这是Spring应用中开发切面的基础。
     包括的jar文件:aop, aspects
   (3)数据访问与集成:提供了多种和数据库进行交互的方式,比如自带的JDBC,集成的包括ORM方式等
     包括的jar文件:jdbc, orm, transaction, jms, messaging, oxm
   (4)web与远程调用:自带的mvc框架有助于在web层提升应用的松耦合水平;
                    远程调用可以实现与其它应用的交互。远程调用的功能spring既自己实现了一个框架,也集成了一些其它的框架。
       包括的jar文件:web ,webmvc, webmvc-portlet, websocket
   (5)Instrumentation:此模块提供了为JVM添加代理的功能。具体就是:为tomcat提供了一个织入代理,能够为tomcat传递类文件,就像这些文件是被类加载器加载进去的。
       包括的jar文件:instrument, instrucment-tomcat
   (6)测试:对单元测试提供了一系列的mock实现;
           对于集成测试,该模块为加载spring应用上下文中的bean集合以及spring上下文中的bean进行交互提供了支持。
   
5、Spring新特性:
这里只写几个对于我来说不够特别熟悉的地方。
(1)spring3.1添加了@profile注解,从而解决各种环境下(如开发、测试、生产)选择不同配置的问题。这样bean的创建就能根据当前选择了什么环境,从而决定要不要被创建。
(2)spring3.0添加了许多enable功能,通过这个注解可以启用很多spring特定的功能。
(3)spring3.1添加了对声明式缓存的支持。
(4)spring4.0添加了对websocket编程的支持。
(5)spring4.0是第一批支持java8特性的java框架
(6)spring4.0添加了条件化创建bean的功能。
作者:caoxiaohong1005 发表于2017/11/10 9:06:17 原文链接
阅读:0 评论:0 查看评论

BZOJ1597[Usaco2008 Mar]土地购买(洛谷P2900)

$
0
0

斜率优化DP

BZOJ题目传送门
洛谷题目传送门

先对土地按照长和宽进行排序,把长和宽被包括的土地(即对于土地i,存在土地j,i.xj.xi.yj.y)删去,对剩下的进行DP:

f[i]表示以i为结尾的土地所需的最小代价,则
f[i]=min(f[j]+x[i]y[j+1])
即把j+1~i进行合并。

但是这样是n2的,注意到有x[i]y[j+1]存在,那么考虑斜率优化。
下面是我的推导过程:

f[x]+x[i]y[x+1]<f[y]+x[i]y[y+1]

f[x]f[y]<x[i](y[y+1]y[x+1])

f[x]f[y]y[y+1]y[x+1]<x[i]

单调队列维护左边的式子即可。

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#define MAXN 50000
using namespace std;
typedef long long LL;
struct lnd{
    LL x,y;
}a[MAXN+5],b[MAXN+5],c[MAXN+5];
int na,nb,n,r,w,que[MAXN+5];
LL f[MAXN+5];
bool cmp(lnd x,lnd y){
    return x.x<y.x||(x.x==y.x&&x.y<y.y);
}
LL K(int x,int y){
    return (f[x]-f[y])/(c[y+1].y-c[x+1].y); 
}
int main(){
    scanf("%d",&na);
    for (int i=1;i<=na;i++)
        scanf("%lld%lld",&a[i].x,&a[i].y);
    sort(a+1,a+na+1,cmp);
    for (int i=1;i<=na;i++){
        while (n&&a[i].y>=c[n].y) n--;
        c[++n]=a[i];
    }
    for (int i=1;i<=n;i++){
        while (r<w&&K(que[r],que[r+1])<c[i].x) 
            r++;
        f[i]=f[que[r]]+c[i].x*c[que[r]+1].y;
        while (r<w&&K(que[w-1],que[w])>=K(que[w],i)) w--;
        que[++w]=i;
    }
    return printf("%lld\n",f[n]),0;
} 
作者:a1799342217 发表于2017/11/9 22:09:36 原文链接
阅读:2 评论:0 查看评论

【网络编程】处理定时事件(三)---看看Libco的时间轮

$
0
0

前言

你以为我鸽了其实我没有鸽,这也算是一种鸽。
继续来填坑啦。

在上两篇中,我们都是使用的链表进行保存定时事件,当我们需要增加一个或者删除一个事件时都需要O(n)的时间复杂度,本篇我们通过时间轮(time wheel)这种数据结构来对其进行优化,而libco也是通过时间轮来进行处理的,所以就拿着它的代码来讲啦。

正文

Libco的作为一个协程库,相当于在用户态完成了逻辑流的切换,这里的调度便是一旦遇到阻塞的系统调用(如read)时,将其注册到epoll_wait中并切换逻辑流,等待其I/O事件的到达,一旦到达则进行处理,将同步阻塞I/O换成了I/O多路复用。

而这里便是将I/O事件当作定时事件来处理,将I/O事件设置超时事件,如果超时则直接处理,避免一直等待的情况。

libco管理定时事件便是使用时间轮这种数据结构,通过一种hash的思想使得添加定时事件的时间复杂度降到O(1),大大提高了效率。
我们先来看看时间轮是怎样的东西。

时间轮是个啥

在之前我们通过链表,按照超时时间进行升序或者降序的排列,这样添加事件就需要O(N)的时间复杂度。
而时间轮则将多条链表组合起来,每条链表上的事件都是同样的超时时间,而两条链表超时时间的差值t就是处理超时事件的时间间隔。时间轮内部有一个指针指向当前的链表,t时间过去,t指向下一个链表,判断是否超时。
而当我们想要添加一个定时事件,只需要知道它的超时时间,再除以t,就是它应该插入的位置。

如图,当前指向1号链表,t为50ms,当需要添加一个定时为100ms的定时事件时,直接添加到3号链表即可(O(1))。
(图转自https://www.ibm.com/developerworks/cn/linux/l-cn-timers/index.html
pic

libco的主循环分析

让我们看看这里的主循环,为了思路清晰,删除部分无关代码

void co_eventloop( stCoEpoll_t *ctx,pfn_co_eventloop_t pfn,void *arg )
{

    co_epoll_res *result = ctx->result;


    for(;;)
    {
    /*在之前的博客中,为了达到定时查看的效果,我们使用epoll_wait的超时参数或者定时信号,而这里则是让epoll_wait以一个非常短的间隙(1ms)返回*/
        int ret = co_epoll_wait( ctx->iEpollFd,result,stCoEpoll_t::_EPOLL_SIZE, 1 );

        stTimeoutItemLink_t *active = (ctx->pstActiveList);
        stTimeoutItemLink_t *timeout = (ctx->pstTimeoutList);

        memset( timeout,0,sizeof(stTimeoutItemLink_t) );

        for(int i=0;i<ret;i++)
        {
            stTimeoutItem_t *item = (stTimeoutItem_t*)result->events[i].data.ptr;
            if( item->pfnPrepare )//如果有预处理函数则调用预处理函数
            {
                item->pfnPrepare( item,result->events[i],active );
            }
            else
            {
                AddTail( active,item );//否则添加到active链,准备下一步处理
            }
        }


        unsigned long long now = GetTickMS();//获取现在的时间,这里我们下文会叙述。
        TakeAllTimeout( ctx->pTimeout,now,timeout );//将时间轮上的超时事件取出,并且让时间轮向前滚动

        stTimeoutItem_t *lp = timeout->head;
        while( lp )
        {
            //printf("raise timeout %p\n",lp);
            lp->bTimeout = true;
            lp = lp->pNext;
        }

        Join<stTimeoutItem_t,stTimeoutItemLink_t>( active,timeout );

        lp = active->head;
        while( lp )
        {

            PopHead<stTimeoutItem_t,stTimeoutItemLink_t>( active );
            if( lp->pfnProcess )
            {
                lp->pfnProcess( lp );//处理active链上的事件
            }
            lp = active->head;
        }
    }
}

可以看到这个eventlopp和我之前几篇博客的思路差不多,都是:
epoll_wait监听–>等待事件–>处理I/O事件–>得到现在时间,判断是否超时–>处理超时事件。

获取现在的时间

这里比较有趣的是GetTickMS,这个用于获取现在时间的函数,


static unsigned long long GetTickMS()
{
#if defined( __LIBCO_RDTSCP__) 
    static uint32_t khz = getCpuKhz();//法1
    return counter() / khz;
#else
    struct timeval now = { 0 };
    gettimeofday( &now,NULL );//法2 使用gettimeofday
    unsigned long long u = now.tv_sec;
    u *= 1000;
    u += now.tv_usec / 1000;
    return u;
#endif
}

gettimeofday自然不用多说,它的好处是跨平台,不用切换到内核态。

而上面的法1使用的函数如下

#if defined( __LIBCO_RDTSCP__) 
static unsigned long long counter(void)
{
    register uint32_t lo, hi;
    register unsigned long long o;
    //__asm__ 内嵌汇编代码 __volatile__阻止编译器优化
    __asm__ __volatile__ (
            "rdtscp" : "=a"(lo), "=d"(hi)
            );//eax寄存器的值赋给lo,edx赋给hi
    o = hi;//o为64位,将hi先放在低32位
    o <<= 32;//移到高位
    return (o | lo);//将lo放在低32位return

}
static unsigned long long getCpuKhz()
{
    FILE *fp = fopen("/proc/cpuinfo","r");
    if(!fp) return 1;
    char buf[4096] = {0};
    fread(buf,1,sizeof(buf),fp);
    fclose(fp);

    char *lp = strstr(buf,"cpu MHz");
    if(!lp) return 1;
    lp += strlen("cpu MHz");
    while(*lp == ' ' || *lp == '\t' || *lp == ':')
    {
        ++lp;
    }

    double mhz = atof(lp);
    unsigned long long u = (unsigned long long)(mhz * 1000);
    return u;
}
#endif

如果你看不懂getCpuKhz这个函数,可以打开/proc/cpuinfo看一眼,就可以知道这里记载的是cpu的动态信息。

而counter函数则主要是调用rdtscp这条汇编指令,将计数(来一个时钟脉冲+1)读出来。
我的理解是counter()将总共的时钟脉冲数读出再除以cpu的频率(每秒时钟脉冲)就是时间,但是cpu的频率不是个恒定值啊,对于现代cpu来说。。。所以不是很明白这里为什么要这样计时,希望能有人给出解答

参考资料

C++开源协程库libco-原理与应用 — 滴滴平台技术部·王亮
__asm__ __volatile__ 的含义 —– stackoverflow
Linux 下定时器的实现方式分析(时间轮部分) — 赵军
Understanding Processor Frequency part of cat /proc/cpuinfo
再论 Time stamp counter —– 一念天堂的博客
如何精确测量一段代码的执行时间 —– 浅墨的部落格

作者:XiyouLinux_Kangyijie 发表于2017/11/9 22:19:30 原文链接
阅读:1 评论:0 查看评论

BZOJ1602 [Usaco2008 Oct]牧场行走

$
0
0

标签:,LCA

Description

N头牛(2<=n<=1000)别人被标记为1到n,在同样被标记1到n的n块土地上吃草,第i头牛在第i块牧场吃草。 这n块土地被n-1条边连接。 奶牛可以在边上行走,第i条边连接第Ai,Bi块牧场,第i条边的长度是Li(1<=Li<=10000)。 这些边被安排成任意两头奶牛都可以通过这些边到达的情况,所以说这是一棵树。 这些奶牛是非常喜欢交际的,经常会去互相访问,他们想让你去帮助他们计算Q(1<=q<=1000)对奶牛之间的距离。

Input

*第一行:两个被空格隔开的整数:N和Q

 *第二行到第n行:第i+1行有两个被空格隔开的整数:AI,BI,LI

*第n+1行到n+Q行:每一行有两个空格隔开的整数:P1,P2,表示两头奶牛的编号。

Output

*第1行到第Q行:每行输出一个数,表示那两头奶牛之间的距离。

Sample Input

4 2

2 1 2

4 3 2

1 4 3

1 2

3 2

Sample Output

2

7

 

模板水题

再一次珍惜刷水机会

Code

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<algorithm>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define dep(i,a,b) for(int i=a;i>=b;i--)
#define ll long long
#define mem(x,num) memset(x,num,sizeof x)
#ifdef WIN32
#define LL "%I64d\n"
#else
#define LL "%lld\n"
#endif
using namespace std;
inline ll read()
{
	ll f=1,x=0;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
const int maxn=1006;
struct edge{int to,next,w;}e[maxn<<1];
bool vis[maxn];
ll n,q,deep[maxn],dis[maxn],fa[maxn][11],last[maxn],cnt=0;

void dfs(int x)
{
	vis[x]=1;
	rep(i,1,10){
		if(deep[x]<(1<<i))break;
		fa[x][i]=fa[fa[x][i-1]][i-1];
	}
#define reg(x) for(int i=last[x];i;i=e[i].next)
#define v e[i].to
    reg(x){
    	if(vis[v])continue;
    	deep[v]=deep[x]+1;
    	dis[v]=dis[x]+e[i].w;
    	fa[v][0]=x;
    	dfs(v);
    }
}
int lca(int x,int y)
{
	if(deep[x]<deep[y])swap(x,y);
	int t=deep[x]-deep[y];
	rep(i,0,10)
	    if((1<<i)&t)x=fa[x][i];
	dep(i,10,0)
	    if(fa[x][i]!=fa[y][i])x=fa[x][i],y=fa[y][i];
	if(x==y)return x;
	else return fa[x][0];
}
int main()
{
	n=read(),q=read();
	rep(i,1,n-1){
		int x=read(),y=read(),z=read();
		e[++cnt]=(edge){y,last[x],z};last[x]=cnt;
		e[++cnt]=(edge){x,last[y],z};last[y]=cnt;
	}//cout<<"R";
	dfs(1);
	rep(i,1,q){
		int x=read(),y=read();
		printf(LL,dis[x]+dis[y]-2*dis[lca(x,y)]);
	}
	return 0;
}


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


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