精华内容
下载资源
问答
  • 循环指令for的工作原理
    千次阅读
    2019-05-17 16:31:17

    一、增强for循环

    1. 三种常用for循环

    // 普通for循环遍历
    for (int i = 0; i < list.size(); i++) {
       System.out.print(list.get(i) + ",");
    }
    // 迭代器循环遍历
    Iterator iterator = list.iterator();
    while (iterator.hasNext()) {
       System.out.print(iterator.next() + ",");
    }
    // 增强for循环
    for (Integer i : list) {
       System.out.print(i + ",");
    }

    2. 增强for循环实现原理

    编译前:

    for (Integer i : list) {
       System.out.print(i + ",");
    }

    编译后:

    Integer i;
    for(Iterator iterator = list.iterator(); iterator.hasNext(); System.out.println(i)){
       i = (Integer)iterator.next();        
    }

    源码解析:

    Integer i; // 定义一个临时变量i
    Iterator iterator = list.iterator(); // 获取List的迭代器
    iterator.hasNext(); // 判断迭代器中是否有未遍历过的元素
    i = (Integer)iterator.next(); // 获取第一个未遍历的元素,赋值给临时变量i
    System.out.println(i) // 输出临时变量i的值

    通过反编译源码,我们看到,其实JAVA中的增强for循环底层是通过迭代器模式来实现的。

    所以以下的代码:

    Iterator<Map.Entry<String, Integer>> entries = myMap.entrySet().iterator();
    while (entries.hasNext()) {
        Map.Entry<String, Integer> entry = entries.next();
        String key = entry.getKey();
        Integer value = entry.getValue();
    }

     可以简化为:

    for(Map.Entry<String, Integer> entry : myMap.entrySet()) {
        String qingActivityId = entry.getKey();
        Integer value = entry.getValue();
    }

     


    3. 注意:增强for循环可能遇到的坑

    既然增强for循环通过迭代器实现,那么必然有迭代器的特性。

    Java中有fail-fast机制。在使用迭代器遍历元素的时候,在对集合进行删除的时候一定要注意,使用不当有可能发生ConcurrentModificationException,这是一种运行时异常,编译期并不会发生。只有在程序真正运行时才会爆发。

    // 代码示例
    for (UserInfo user : userInfos) {    
       if (user.getId() == 2)     
          userInfos.remove(user);    
    }

     会抛出ConcurrentModificationException异常。

    Iterator是工作在一个独立的线程中,并且拥有一个 mutex 锁。 Iterator被创建之后会建立一个指向原来对象的单链索引表,当原来的对象数量发生变化时,这个索引表的内容不会同步改变,所以当索引指针往后移动的时候就找不到要迭代的对象,所以按照 fail-fast 原则 Iterator 会马上抛出
    java.util.ConcurrentModificationException异常。

    所以 Iterator 在执行的时候是不允许被迭代的对象被改变的

    删除对象的正确做法:

    你可以使用 Iterator 本身的方法 remove() 来删除对象,Iterator.remove() 方法会在删除当前迭代对象的同时维护索引的一致性。

    正确的在遍历的同时删除元素的示例:

    Iterator<UserInfo> userIterator = users.iterator();    
    while (userIterator.hasNext()) {    
       UserInfo userInfo = userIterator.next();    
       if (userInfo.getId() == 2)    
           userIterator.remove();//这里要使用Iterator的remove方法移除当前对象,如果使用List的remove方法,则同样会出现ConcurrentModificationException    
    }

     

    二、for循环实战性能优化

    循环结构让我们操作数组、集合和其他一些有规律的事物变得更加的方便,但是如果我们在实际开发当中运用不合理,可能会给程序的性能带来很大的影响。所以我们还是需要掌握一些技巧来优化我们的代码的。

    1. 嵌套循环

    1.1 代码示例

    优化前代码示例

    Long stratTime = System.nanoTime();  
    for (int i = 0; i < 10000; i++) {  
        for (int j = 0; j < 10; j++) {  
              
        }  
    }  
    Long endTime = System.nanoTime();  
    System.out.println("外大内小耗时:"+ (endTime - stratTime));   

    优化后代码示例

    Long  stratTime = System.nanoTime();  
    for (int i = 0; i <10 ; i++) {  
        for (int j = 0; j < 10000; j++) {  
              
        }  
    }  
    Long  endTime = System.nanoTime();  
    System.out.println("外小内大耗时:"+(endTime - stratTime));  

    运行结果:

    外大内小耗时:1957590
    外小内大耗时:1228223

    由运行结果来看采用外大内小的方式性能差距还是比较大的。

    1.2 原理

    如果遇到分支结构,就可以利用分支目标缓冲器预测并读取指令的目标地址。分支目标缓冲器在程序运行时将动态记录和调整转移指令的目标地址,可以记录多个地址,对其进行表格化管理。当发生转移时,如果分支目标缓冲器中有记录,下一条指令在取指令阶段就会将其作为目标地址。如果记录地址等于实际目标地址,则并行成功;如果记录地址不等于实际目标地址,则流水线被冲洗。同一个分支,多次预测失败,则更新记录的目标地址。因此,分支预测属于“经验主义”或“机会主义”,会存在一定误测。
    ————摘抄来源<<C++反汇编与逆向分析技术解密>> 4.4.2 分支优化规则

    1.3 原理解析

    // 外小内大
    for (int i = 0; i <10 ; i++) {  
        // 下面每次循环会预测成功9999次
        // 第1次没有预测,最后退出循环时预测失败1次
        // 这样的过程重复10次
        for (int j = 0; j < 10000; j++) {  
              a[i][j]++;
        }  
    }  
    // 外大内小
    for (int i = 0; i < 10000; i++) {  
        // 下面每次循环会预测成功9次
        // 第1次没有预测,最后退出循环时预测失败1次
        // 这样的过程重复10000次
        for (int j = 0; j < 10; j++) {  
              a[i][j]++;
        }  
    }  

    2. 消除循环终止判断时的方法调用

    2.1 代码示例

    未优化前代码示例

    Long stratTime = System.nanoTime();  
    for (int i = 0; i < list.size(); i++) {  
          
    }  
    Long  endTime = System.nanoTime();  
    System.out.println("未优化list耗时:"+(endTime - stratTime));  

    优化后代码示例

    Long  stratTime = System.nanoTime();  
    int size = list.size();  
    for (int i = 0; i < size; i++) {  
          
    }  
    Long  endTime = System.nanoTime();  
    System.out.println("优化list耗时:"+(endTime - stratTime)); 

    运行结果

    未优化list耗时:27375  
    优化list耗时:2444

    2.2原理

    list.size()每次循环都会被执行一次,这无疑会影响程序的性能,所以应该将其放到循环外面,用一个变量来代替,优化前后的对比也很明显。


    3. 异常捕获

    3.1 代码示例

    优化前代码示例

    Long stratTime = System.nanoTime();  
    for (int i = 0; i < 10000000; i++) {  
        try {  
        } catch (Exception e) {  
        }  
    }  
    Long  endTime = System.nanoTime();  
    System.out.println("在内部捕获异常耗时:"+(endTime - stratTime));  

    优化后代码示例

    Long  stratTime = System.nanoTime();  
    try {  
        for (int i = 0; i < 10000000; i++) {  
        }  
    } catch (Exception e) {  
      
    }  
    Long  endTime = System.nanoTime();  
    System.out.println("在外部捕获异常耗时:"+(endTime - stratTime));  

    运行结果

    在内部捕获异常耗时:12150142  
    在外部捕获异常耗时:1955  

    3.2 总结

    捕获异常是很耗资源的,所以不要讲try catch放到循环内部,优化后同样有好几个数量级的提升。

     


    来源于:

    https://www.jianshu.com/p/f7c82aa62439

    更多相关内容
  • 很多初学者对于程序中ms级延时函数的编写方法比较疑惑,其原理和方式虽然简单,但是却没有相关资料。这里用一个例程详细介绍一下。
  • for while循环练习

    千次阅读 2022-04-01 14:20:52
    2022.3.31 range函数 range的用法 注意:3.x之后前面得加一个list(range())要不无法输出 range(stop): 0~stop-1 range(start,stop): start~stop-1 range(start,stop,step): start~stop step(步长) for 循环 for 循环...

    2022.3.31
    range函数
    range的用法 注意:3.x之后前面得加一个list(range())要不无法输出
    range(stop): 0~stop-1
    range(start,stop): start~stop-1
    range(start,stop,step): start~stop step(步长)

    for 循环
    for 循环使用的语法:(for循环可以加else语句,else语句后跟循环结束需要执行的代码,)
    break 语句可以用于跳出循环,可以用在 for 循环、while 循环、switch 循环中。当我们在一个循环中使用了break 语句,则会中断循环,但是仍会继续执行循环之后的代码。
    if 条件判断也能用break
    for 变量 in range(10):
    循环需要执行的代码
    else:
    循环结束时,需要执行的代码

    练习:
    例子1.分别求1~100内偶数之和

    #利用for循环求1~100内偶数之和
    sum = 0
    for i in range(0,101,2):#取偶数操作由步长为2的数列操作完成
        sum = sum+i
        print(sum)#for循环内输出会将每个结果都打印出来
    print(sum)#for循环之外输入只输出最终结果
    一定仔细思考print在不同位置时的输出
    0
    2
    6
    12
    20
    30
    42
    ...
    2256
    2352
    2450
    2550
    
    2550
    

    例子2:求阶乘即n!
    (本例亦可使用递归函数完成)

    def jiecheng(s):
        s = int(s)
        if s == 1 or s == 0:
            return 1
        else:
            return (s*jiecheng(s-1))
    

    for循环的嵌套
    例子3. 有1,2,3,4四个数字,求这四个数字能生成多少个互不相同且无重复数字的三位数

    sum = 0
    for i in (1,2,3,4):
        for j in (1,2,3,4):
                for k in (1,2,3,4):
                            if i != j and j!= k and i != k: 
                                           sum += 1
    print('共有%d种组合' %sum)
    

    例子4. 有1,2,3,4四个数字,求这四个数字能生成多少个互不相同且无重复数字的三位数,并展示出来

    for x in range(1,5):
        for y in range(1,5):
            for z in range(1,5):
                    if (x!=y) and (y!=z) and (z!=x):
                            print("%d%d%d" % (x, y, z))
    

    实现命令行提示符
    break:跳出整个循环,不会再执行循环后续的内容
    continue:跳出本次循环,continue后面的代码不再执行,但是还是会继续循环
    exit():结束程序的运行
    例子5. 打印99乘法表

    for i in range(1,10):
        for j in range(1,i+1):
                print('%d * %d = %d\t' %(i,j,i*j),end='')
                #\t制表符,自动对齐后面的东西 end=‘’表示将输出 如果end='\n'则表示换行
        print()#表示执行完之前的print后换行 
    

    print函数的用法

    注意理解此代码中while的嵌套 嵌套时代码的退格操作
    temp = input('请输入一个整数:')
    number = int(temp)
    while number:
        i = number - 1
        while i:
            print('+', end='')
            i = i - 1
        j = number
        while j:
            print('*', end='')
            j = j - 1
        #print()#不加print()函数,则所有输出为一行
        number = number - 1
        不加print()时的输出 
        ++***+***print()的输出 
        ++***
        +**
        *
    

    例子6:用户登录管理系统,如下三点要求
    1.输入用户名和密码;
    2.判断用户名和密码是否正确(name=‘root’,passwd=‘westos’)
    3.登录仅有三次机会,超过3次会报错

    注意理解此代码中for循环与else语句的使用
    for i in range(1,4):#先用for循环限定登录次数
        user = input('请输入用户名:')
        password = input('请输入密码:')
        if user == 'root' or password == 'huahua':
            print('%s登录成功!'%user)
            break
        else:#循环内的if else语句需要对齐
            print('用户名或密码输入错误,请重新输入!',end='')#使两条print的语句输出为一行
            print('您还有%d次机会'%(3-i))
    else:#当循环内的所有代码(包括所有的if else语句)执行完之后,输出print之后的语句
        print('错误次数已超过三次,登录失败')#else的空格与for对齐    
    

    例子7:求最大公约数和最小公倍数,如下三点要求
    输入两个数值:
    求两个数的最大公约数和最小公倍数
    最小公倍数=(num1 * num2) / 最大公约数

    # 输入两个数字
    num1=int(input('Num1:'))
    num2=int(input('Num2:')) 
    # 找出两个数中的较小者
    min_num = min(num1,num2) 
    # 确定最大公约数
    # 思考该函数怎么能找出最大公约数
    for i in range(1,min_num+1):    
        if num1 % i ==0 and num2 % i ==0:        
            max_commer = i
    # 求最小公倍数
    min_commer =int(num1 * num2)/max_commer 
    print('%s 和 %s 的最大公约数为%s' %(num1,num2,max_commer))
    print('%s 和 %s 的最小公倍数为%s' %(num1,num2,min_commer))
    

    例子7的发散:
    求两个数的所有公约数,并输出最大、最小公约数

    #求两个数的所有公约数,输出所有公约数并单独输入最大公约数
    num1 = int(input('请输入一个整数:'))
    num2 = int(input('请输入一个整数:'))
    min_num = min(num1,num2)
    L = []
    for i in range(1,min_num+1):
        #L = []#L定义在循环内时,每次循环会对列表进行重新赋值,导致输出只输出一个数
        if num1 % i == 0 and num2 % i ==0:
            #L = []
            L.append(i)
            #print(i)
            #L.append(i)
            #print(L)
            #print(id(L))
        #print(L)#在此加print函数会导致每次循环结束后都进行输出,
    else:#与for循环相同的位格上写else函数,执行for循环结束后的结果
        print(L)
        L.sort()
        print(L[0],L[-1])
    #这两种代码输出结果是一样的
    print(L)
    L.sort()
    #L = L.sort()#对列表排序直接使用L.sort()即可,不能使用诸如L = L.sort的语句
    print(L[0],L[-1])
    

    二、while循环
    循环的作用就是让指定的代码重复的执行,while 循环最常用的应用场景就是让执行的代码按照指定的次数重复执行

    while 条件():    
        条件满足时,做的事情1    
        条件满足时,做的事情2
        ......
    

    1、基本用法
    注意:一定要注意循环内的退格用法

    # 定义一个整数变量,记录循环的次数
    i=1
    # 开始循环
    while i <= 3:    
    # 希望循环内执行的代码    
        print('hello python')    
        # 处理计数器
        i+=1
    

    死循环的例子:

    while True:
        print('hello python')#会一直循环输出
    

    例子1:3、求1~100之间数字的和

    #求1~100之间数字的和
    i = 1
    sum = 0
    while i <= 100:
        sum = sum+i
        i = i+1
        #print(sum)#注意print的位置
    print(sum)
    

    例子1发散:求100内所有偶数的和

    #发散 while循环求1-100内所有偶数的和
    i = 1
    sum = 0
    while i <= 100:
        if i % 2 == 0:
            sum = sum + i
        i = i+1#注意i = i+1这个式子是与if语句同位格的
    print(sum)
    

    例子2:用户登录管理

    i = 0
    while i <= 3:
        user = input('请输入用户名:')
        password = input('请输入密码:')
        if user == 'root' and password == 'huahua':
            print('%s登录成功!' %user)
            break
        else:
            print('用户名或密码输入错误,请重新输入!',end='')
            print('您还有%d次机会' %(3-i))
        i = i+1
    

    while 循环嵌套
    while 里面还有 while
    例子1:如下要求
    1.在控制台连续输出五行*,每一行星号的数量依次递增
    2.完成 5 行内容的简单输出 分析每行内部的*应该如何处理
    输出
    *
    **




    解决方法1:for循环

    # 1.控制行
        for i in range(6):
            # 2.控制*的数量
            for j in range(6):
                if i > j :
                    print("*",end='')
            print(" ")
    

    解决方法2:for循环

    for i in range(1,6):
        for j in range(1,i+1):
            print('*',end='')
        print()
    

    解决方法3:while循环

    #样式三:
    row = 1
    while row <= 5:      ##行循环5次,即外层的总循环次数
        col = 1
        a = 1            ###控制空格的输出次数
        while a <= 5-row:   ###空格的输出条件
            print(' ',end='')    ##输出空格
            a += 1
        while col <= row:    ###*的输出条件
            print('*',end='')
            col += 1
        print('')         ###行每循环一次使下一次的循环输出换行显示
        row += 1
    

    解决方法4:while循环

    i = 1
    while i <= 5:
        j = 1
        while j <= i:
            print('*',end='')
            #print('*')
            j = j+1
        #print()
        print('')#输出空,将每一行隔开
        #在i=1,j=1(即在每一个i输出完之后,均添加空行)时,输出完所有*之后,再输出空,隔开行。
        i = i + 1
    

    例子1发散:如果想要星星倒过来呢
    解决方法1:for循环

    # 1.控制行
    for i in range(6):
        # 2.控制*的数量
        for j in range(6):
            if i < j :
                print("*",end='')
        print(" ")
    

    解决方法2:while循环

    i = 1
    while i <= 5:
        j = 1
        while j < i:
            print(" ",end='')
            j += 1
        while j >= i and j <= 5:
            print("*",end='')
            j += 1
        print()
        i += 1
    

    解决方法3:while循环

    i = 1
    while i <= 5:
        j = 5
        while j >= i:
            print('*',end='')
            j = j-1
        print('')
        i = i+1
    

    例子2: 输出倒三角

    i = 1
    while i <= 9:
        while i <= 5:
            j = 1
            while j <= i:
                print('*',end='')
                j = j+1
            print('')
            i = i +1
        while 5 < i <= 9:
            j = 10
            while j > i:
                print('*', end='')
                j = j-1
            print('')
            i = i + 1
    

    例子2推广:用*输出一个棱形
    菱形*输出

    row = 1
    while row <= 9:
        col = 1
        star = 1
        # 先计算前五行的
        if row <= 5:
            while col <= (5 - row):
                print(" ", end="")
                col += 1
            while star <= row * 2 - 1:
                print("*", end="")
                star += 1
        # 计算的后四行的
        else:
            while col <= (row - 5):
                print(" ", end="")
                col += 1
            while star <= 19 - row * 2:
                print("*", end="")
                star += 1
        print("")
        row += 1
    

    例子3:猜数字游戏
    猜数字游戏:
    1.系统随机生成一个1~100的数字;
    2.用户共有5次机会猜;
    3.如果用户猜测数字大于系统给出的数字,打印"too big"
    4.如果用户猜测数字小于系统给出的数字,打印"too small"
    5.如果用户猜测的数字等于系统给出的数字,打印"恭喜中奖",并退出循环

    import random
    
    num = random.randint(1,100)
    print(num)
    
    i = 1
    while i <= 5:
        ans = int(input("请猜数:"))
        if ans > num:
            print("too big,还剩%d次机会"%(5-i))
        elif ans < num:
            print("too small,还剩%d次机会"%(5-i))
        else:
            print("恭喜中奖!!!")
            break
        i += 1
    

    while和for的进阶案例 while for嵌套使用
    例子1、 求 1+2!+3!+…+20! 的和。
    解决方法1:while循环嵌套

    i = 1
    sum = 0
    while i <= 20:
        sum1 = 1
        j = 1
        while j <= i:
            sum1 = sum1*j
            j = j+1
        i = i+1
        sum = sum+sum1
        #print(sum) 这里会把每一步的输出都给输出出来
    print(sum)
    

    解决方法2 :for循环嵌套

    for x in range(1, 21):
        sum1 = 1
        for j in range(1, x + 1):
            sum1 = sum1 * j
            j = j + 1
        sum = sum + sum1
    # i = i+1
    print(sum)
    

    例子2:本金10000元存入银行,年利率是千分之三。每过1年,将本金和利息相加作为新的本金。计算5年后,获得的本金是多少?

    #while循环
    i = 1
    t = 10000
    x = 0.003
    while i <= 5:
        t = t*x+t
        i = i+1
    print(t)
    #for循环
    t = 10000
    x = 0.003
    for i in range(1,6):
        t = t+t*x#注意此时for循环只用来判定次数用
    print(t)
    #递归函数
    

    例子3、题目:打印出所有的"水仙花数",所谓"水仙花数"是指一个三位数,其各个位上数字的立方和等于该数本身。例如:153是一个”水仙花数",因为153=1的三次方+5的三次方+3的三次方。
    解决方法1:for循环

    for s in range(100,1000):
        s = str(s)
        s1 = int(s[0])
        s2 = int(s[1])
        s3 = int(s[2])
        x = str(s1**3+s2**3+s3**3)
        if x == s:
            print(x)
    
    for s in range(100,1000):
        if ((s//100)**3)+(((s%100)//10)**3)+((s%10)**3) == s:
            print(s)
    

    解决方法2:while循环

    s = 100
    while s < 1000:
        if ((s//100)**3)+(((s%100)//10)**3)+((s%10)**3) == s:
            print(s)
        s = s+1
    

    例子4:设计“过7游戏”的程序, 打印出1-100之间除了含7和7的倍数之外的所有数字
    解决方法1:while循环

    m = 1
    while m <= 100:
        if m%7 != 0:
            if m//10 != 7:
                if m%10 != 7:
                    print(m)
        m = m + 1
    

    解决方法2 :while循环

     i = 0
     while i <= 99:
         i += 1
         if i % 7 == 0 or i % 10 == 7 or i // 10 == 7:
             continue#while中continue语句的运用
         else:
             print(i)
    

    解决方法3:for循环

    for i in range(1, 101):
        if i % 7 == 0 or i % 10 == 7 or i // 10 == 7:
            continue
        print(i)
    

    例子5:使用while、if来完成剪刀石头布程序,要求,当玩家第3次获胜时才退出游戏,否则继续玩。
    解决方法1:while循环(用while来限定循环次数)

    import random
    i = 1#用while循环记录赢的次数
    while i <= 3:
        player = int(input('请出招(石头0,剪刀1,布2):'))
        computer = random.randint(0, 2)
        if (player == 2 and computer == 0) or (player == 0 and computer == 1) or (player == 1 or computer == 2):
            i = i+1
            print('你赢啦,再来!')
            #print(i)#可以打印出i来观察每次赢之后i值有没有增加
        #i = i+1#如果将此式子写在此处,代表着判定条件的结束,无法进行下面的判定,注意体会
        elif player == computer:
            print('平局啦,再来!')
        else:
            print('你输啦,再来!')
    print('你赢了三次啦,结束!')
    

    解决方法2:while循环(用if语句来判定赢的次数是否到达3次)

    win_times = 0
    while True:
        player = int(input("请输入:剪刀(0) 石头(1) 布(2)"))
        computer = random.randint(0, 2)
        if (player == 0 and computer == 2) or (player == 1 and computer == 0) or (player == 2 and computer == 1):
            win_times += 1
            if win_times == 3:
                print("你赢了三次了")
                break
            else:
                print("你赢了这局游戏")
        elif player == computer:
            print("平局,再来一次")
        else:
            print("你输了,不要走,决战到天亮")
    

    例子6:7、幸运猜猜猜:游戏随机给出一个0~99(包括0和99)的数字,然后让你猜是什么数字。
    你可以随便猜一个数字,游戏会提示太大还是太小,从而缩小结果范围。
    经过几次猜测与提示后,最终推出答案。在游戏过程中,记录你最终猜对时所需要的次数,
    游戏结束后公布结果。
    说明:
    1~2次猜中,打印你太TM有才了!
    3~6次猜中,打印这么快就猜出来了,很聪明嘛!
    大于7次猜中,打印猜了半天才猜出来,小同志,尚需努力啊!
    猜测次数最多20次。
    解决方法:while循环(自己写的)

    import random
    i = 0
    computer = random.randint(0, 99)
    while True:
        player = int(input('请输入你猜的数字:'))
        #computer = random.randint(0, 99)#随机生成一定要在循环外,否则每次都会随机生成
        if i <= 20:
            if player == computer:
    
                if i <= 2:
                    print('你真厉害')
                    break
                elif 3 <= i <= 6:
                    print('你真聪明')
                    break
                elif 7 <= i <= 20:
                    print('再努努力吧')
                    break
            elif player > computer:
                i = i + 1
                print('太大,再猜')
            else:
                i = i + 1
                print('太小,再猜')
        else:
            print('最多只能猜20次哦')
            break
    #else:
    print('你一共猜了%d次'%i,end='')
    print(',正确数字是%s'%(computer))
    

    解决方法2:while循环

    import random
    
    num = random.randint(0, 99)
    print(num)
    times = 0
    
    while times <= 20:
        ges = int(input("请输入你猜的数字: "))
        if ges > num:
            print("你猜的数太大了")
            times += 1
            if times == 20:
                print("你都猜了20次了,还没猜中")
                break
            continue
        elif ges < num:
            print("你猜的数字太小了")
            times += 1
            if times == 20:
                print("你都猜了20次了,还没猜中")
                break
            continue
        else:
            if 1 <= times <= 2:
                print("有才")
            elif 3 <= times <= 6:
                print("还可以")
            else:
                print("需要努力")
    
    print(num)
    
    
    展开全文
  • Vue自定义指令介绍及原理

    千次阅读 2022-04-02 16:10:38
    Vue自定义指令 Vue指令: 在使用Vue框架进行前端开发时,我们经常会使用一些特殊指令来快速实现一些效果或功能。 常见指令如:v-bind、v-if (v-else)、v-show、v-html等等都是一些比较常用的指令 由于本文主要介绍...

    Vue自定义指令

    Vue指令:

    在使用Vue框架进行前端开发时,我们经常会使用一些特殊指令来快速实现一些效果或功能。
    常见指令如:v-bind、v-if (v-else)、v-show、v-html等等都是一些比较常用的指令
    使用如下:

    <span v-if=true v-html="htmlContext || '--'"></span>
    <span v-show=true> show html </span>
    

    由于本文主要介绍自定义指令相关的一些知识,所以对于Vue自带指令就不做过多赘述了
    在这情况下,Vue官方也推出了一种编写自定义指令的方法。我们可以定义开发我们自定义的模版指令,来对一些特殊的需求效果功能进行开发与实现
    在Vue的官方文档中是如下描述的:

    vue自定义指令官方文档在这里插入图片描述

    自定义指令不只支持全局注册,也支持在组件中进行注册并引用,具体操作在文档中都有详细描述

    Vue自定义指令

    Vue自定义指令机制包含五个钩子函数,每个钩子函数包含四个回调参数
    钩子函数简单来说可以称作为生命周期,如果对生命周期有过了解或应用应该会比较容易理解

    Vue2钩子函数:

    bind:只调用一次,组件初始化时会调用该钩子函数
    inserted:被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。
    update:所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生了改变,也可能没有。可以通过比较更新前后的值来忽略不必要的模板更新
    componentUpdated:指令所在组件的 VNode 及其子 VNode 全部更新后调用
    unbind:只调用一次,指令与元素解绑时调用

    Vue3钩子函数:

    created - 新增!在元素的 attribute 或事件监听器被应用之前调用。
    bind → beforeMount
    inserted → mounted
    beforeUpdate:新增!在元素本身被更新之前调用,与组件的生命周期钩子十分相似。
    update → 移除!该钩子与 updated 有太多相似之处,因此它是多余的。请改用 updated
    componentUpdated → updated
    beforeUnmount:新增!与组件的生命周期钩子类似,它将在元素被卸载之前调用
    unbind -> unmounted

    四个函数的回调参数:el、binding、vnode、oldVnode
    el:指令所绑定的元素
    binding(对象):
    name(指令名称)、value(指令的绑定值)、oldValue(指令绑定的前一个值)、
    expression(绑定指令的表达式)、arg(绑定的指令参数)、
    modifiers(一个包含修饰符的对象。例如:v-my-directive.foo.bar 中,修饰符对象为 { foo: true, bar: true })
    vnode:即Vue虚拟DOM生成的虚拟节点,细节可学习Vue虚拟DOM原理
    oldVnode:上一个虚拟节点,仅在 update 和 componentUpdated 钩子函数中可用

    接下来上一个简单的组件注册自定义指令的例子:
    效果为一渲染input元素后即添加焦点,可辅助进行表单校验

    // html
    <input v-focus> 
    
    //  JS
        directives: {
          focus: {
            // 指令的定义
            inserted: function (el,binding,vnode,oldVnode) {
              el.focus();
              // 展示回调值输出
              console.log('el',el);
              console.log('binding',binding);
              console.log('vnode',vnode);
              console.log('oldVnode',oldVnode);
            }
          }
        }
    

    输出如下:
    Vue自定义指令输出回调参数

    Vue自定义指令原理:

    以上介绍了Vue自定义指令的相关介绍以及用法
    接下来将集中分享一下关于Vue自定义指令的实现原理
    首先我们知道,
    Vue一个机制首先要在 vm.$options 上进行挂载 => 而后进行正则解析处理 => 最后生成虚拟dom

    这个的流程就不在此赘述了,有时间会有集中出一篇关于这方面的文章
    接下来我们直接分析patchDom的过程
    源码链接:Vue自定义指令源码 Github
    为了页面不占用太大篇幅,源码就不全额摘抄了,下面将集中分析部分关键原理

    /* @flow */
    
    import { emptyNode } from 'core/vdom/patch'
    import { resolveAsset, handleError } from 'core/util/index'
    import { mergeVNodeHook } from 'core/vdom/helpers/index'
    
    export default {
      create: updateDirectives,
      update: updateDirectives,
      destroy: function unbindDirectives (vnode: VNodeWithData) {
        updateDirectives(vnode, emptyNode)
      }
    }
    
    function updateDirectives (oldVnode: VNodeWithData, vnode: VNodeWithData) {
      if (oldVnode.data.directives || vnode.data.directives) {
        _update(oldVnode, vnode)
      }
    }
    
    // 比较前后节点更新 相关流程
    function _update (oldVnode, vnode) {
      // 是否为新节点 ? 
      const isCreate = oldVnode === emptyNode
      const isDestroy = vnode === emptyNode
      // 新旧节点的指令抽离
      const oldDirs = normalizeDirectives(oldVnode.data.directives, oldVnode.context)
      const newDirs = normalizeDirectives(vnode.data.directives, vnode.context)
    
      // inserted 需要触发钩子函数的元素列表
      const dirsWithInsert = []
      // componentUpdated
      const dirsWithPostpatch = []
    
      // 更新新旧节点 指令
      let key, oldDir, dir
      for (key in newDirs) {
        oldDir = oldDirs[key]
        dir = newDirs[key]
      // 不存在旧节点时,直接插入!
        if (!oldDir) {
          // new directive, bind
          callHook(dir, 'bind', vnode, oldVnode)
          if (dir.def && dir.def.inserted) {
            dirsWithInsert.push(dir)
          }
      // 存在旧节点时:更新      
        } else {
          // existing directive, update
          dir.oldValue = oldDir.value
          dir.oldArg = oldDir.arg
          callHook(dir, 'update', vnode, oldVnode)
          if (dir.def && dir.def.componentUpdated) {
            dirsWithPostpatch.push(dir)
          }
        }
      }
    
      if (dirsWithInsert.length) {
        const callInsert = () => {
          for (let i = 0; i < dirsWithInsert.length; i++) {
            callHook(dirsWithInsert[i], 'inserted', vnode, oldVnode)
          }
        }
        // 判断是否为新创建的节点,如果是,则使用该方法将钩子函数合并,并推迟执行
        if (isCreate) {
          mergeVNodeHook(vnode, 'insert', callInsert)
        } else {
          callInsert()
        }
      }
    
      if (dirsWithPostpatch.length) {
        mergeVNodeHook(vnode, 'postpatch', () => {
          for (let i = 0; i < dirsWithPostpatch.length; i++) {
            callHook(dirsWithPostpatch[i], 'componentUpdated', vnode, oldVnode)
          }
        })
      }
      // 判断该节点是否为新创建的虚拟节点?
      if (!isCreate) {
        // 遍历旧节点
        for (key in oldDirs) {
          // 寻找不存在的节点
          if (!newDirs[key]) {
            // 执行指令的unbind方法
            callHook(oldDirs[key], 'unbind', oldVnode, oldVnode, isDestroy)
          }
        }
      }
    }
    
    const emptyModifiers = Object.create(null)
    
    function normalizeDirectives (
      dirs: ?Array<VNodeDirective>,
      vm: Component
    ): { [key: string]: VNodeDirective } {
      const res = Object.create(null)
      if (!dirs) {
        // $flow-disable-line
        return res
      }
      let i, dir
      for (i = 0; i < dirs.length; i++) {
        dir = dirs[i]
        if (!dir.modifiers) {
          // $flow-disable-line
          dir.modifiers = emptyModifiers
        }
        res[getRawDirName(dir)] = dir
        dir.def = resolveAsset(vm.$options, 'directives', dir.name, true)
      }
      // $flow-disable-line
      return res
    }
    
    // 获取指令name
    function getRawDirName (dir: VNodeDirective): string {
      return dir.rawName || `${dir.name}.${Object.keys(dir.modifiers || {}).join('.')}`
    }
    // 用于循环调用钩子函数
    function callHook (dir, hook, vnode, oldVnode, isDestroy) {
      const fn = dir.def && dir.def[hook]
      if (fn) {
        try {
          fn(vnode.elm, dir, vnode, oldVnode, isDestroy)
        } catch (e) {
          handleError(e, vnode.context, `directive ${dir.name} ${hook} hook`)
        }
      }
    }
    
    展开全文
  • 编译原理课程设计,for循环语句的实现简单优先法,三地址码输出
  • 前言 循环就是让我们的程序...其中,for循环就是循环结构的一种,另外还有while循环和do-while循环语句。但是for循环是开发者最常用的开发方式。 一、增强for循环1. 三种常用for循环#普通for循环遍历 for (int i = 0;

    IT实战联盟博客:http://blog.100boot.cn
    IT实战联盟.jpg

    前言

    循环就是让我们的程序重复地执行某些业务。在程序设计时,需要处理大量的重复动作,采用循环结构可以降低程序书写的长度和复杂度,可使复杂问题简单化,提高程序的可读性和执行速度。其中,for循环就是循环结构的一种,另外还有while循环和do-while循环语句。但是for循环是开发者最常用的开发方式。

    for循环.jpg

    一、增强for循环

    1. 三种常用for循环

    # 普通for循环遍历
    for (int i = 0; i < list.size(); i++) {
       System.out.print(list.get(i) + ",");
    }
    #迭代器循环遍历
    Iterator iterator = list.iterator();
    while (iterator.hasNext()) {
       System.out.print(iterator.next() + ",");
    }
    #增强for循环
    for (Integer i : list) {
       System.out.print(i + ",");
    }
    

    2. 增强for循环实现原理

    编译前

    for (Integer i : list) {
       System.out.print(i + ",");
    }
    

    编译后

    Integer i;
    for(Iterator iterator = list.iterator(); iterator.hasNext(); System.out.println(i)){
       i = (Integer)iterator.next();        
    }
    

    源码解析

    Integer i; 定义一个临时变量i
    Iterator iterator = list.iterator(); 获取List的迭代器
    iterator.hasNext(); 判断迭代器中是否有未遍历过的元素
    i = (Integer)iterator.next(); 获取第一个未遍历的元素,赋值给临时变量i
    System.out.println(i) 输出临时变量i的值
    

    通过反编译源码,我们看到,其实JAVA中的增强for循环底层是通过迭代器模式来实现的。

    ####3. 注意:增强for循环可能遇到的坑
    既然增强for循环通过迭代器实现,那么必然有迭代器的特性。

    Java中有fail-fast机制。在使用迭代器遍历元素的时候,在对集合进行删除的时候一定要注意,使用不当有可能发生ConcurrentModificationException,这是一种运行时异常,编译期并不会发生。只有在程序真正运行时才会爆发。

    #代码示例
    for (UserInfo user : userInfos) {    
       if (user.getId() == 2)     
          userInfos.remove(user);    
    }
    

    会抛出ConcurrentModificationException异常。

    Iterator是工作在一个独立的线程中,并且拥有一个 mutex 锁。 Iterator被创建之后会建立一个指向原来对象的单链索引表,当原来的对象数量发生变化时,这个索引表的内容不会同步改变,所以当索引指针往后移动的时候就找不到要迭代的对象,所以按照 fail-fast 原则 Iterator 会马上抛出
    java.util.ConcurrentModificationException异常。

    所以 Iterator 在执行的时候是不允许被迭代的对象被改变的。

    但你可以使用 Iterator 本身的方法 remove() 来删除对象,Iterator.remove() 方法会在删除当前迭代对象的同时维护索引的一致性。

    正确的在遍历的同时删除元素的示例:

    Iterator<UserInfo> userIterator = users.iterator();    
    while (userIterator.hasNext()) {    
       UserInfo userInfo = userIterator.next();    
       if (userInfo.getId() == 2)    
           userIterator.remove();//这里要使用Iterator的remove方法移除当前对象,如果使用List的remove方法,则同样会出现ConcurrentModificationException    
    }
    

    二、for循环实战性能优化

    循环结构让我们操作数组、集合和其他一些有规律的事物变得更加的方便,但是如果我们在实际开发当中运用不合理,可能会给程序的性能带来很大的影响。所以我们还是需要掌握一些技巧来优化我们的代码的。

    1. 嵌套循环

    1.1 代码示例

    优化前代码示例

    Long stratTime = System.nanoTime();  
    for (int i = 0; i < 10000; i++) {  
        for (int j = 0; j < 10; j++) {  
              
        }  
    }  
    Long endTime = System.nanoTime();  
    System.out.println("外大内小耗时:"+ (endTime - stratTime));        
    

    优化后代码示例

    Long  stratTime = System.nanoTime();  
    for (int i = 0; i <10 ; i++) {  
        for (int j = 0; j < 10000; j++) {  
              
        }  
    }  
    Long  endTime = System.nanoTime();  
    System.out.println("外小内大耗时:"+(endTime - stratTime));   
    

    运行结果:

    外大内小耗时:1957590
    外小内大耗时:1228223
    

    由运行结果来看采用外大内小的方式性能差距还是比较大的。

    1.2 原理

    如果遇到分支结构,就可以利用分支目标缓冲器预测并读取指令的目标地址。分支目标缓冲器在程序运行时将动态记录和调整转移指令的目标地址,可以记录多个地址,对其进行表格化管理。当发生转移时,如果分支目标缓冲器中有记录,下一条指令在取指令阶段就会将其作为目标地址。如果记录地址等于实际目标地址,则并行成功;如果记录地址不等于实际目标地址,则流水线被冲洗。同一个分支,多次预测失败,则更新记录的目标地址。因此,分支预测属于“经验主义”或“机会主义”,会存在一定误测。
    ————摘抄来源<<C++反汇编与逆向分析技术解密>> 4.4.2 分支优化规则

    1.3 原理解析
    #外小内大
    for (int i = 0; i <10 ; i++) {  
        #下面每次循环会预测成功9999次
        #第1次没有预测,最后退出循环时预测失败1次
        #这样的过程重复10次
        for (int j = 0; j < 10000; j++) {  
              a[i][j]++;
        }  
    }  
    #外大内小
    for (int i = 0; i < 10000; i++) {  
        #下面每次循环会预测成功9次
        #第1次没有预测,最后退出循环时预测失败1次
        #这样的过程重复10000次
        for (int j = 0; j < 10; j++) {  
              a[i][j]++;
        }  
    }  
    

    2. 消除循环终止判断时的方法调用

    2.1 代码示例

    未优化前代码示例

    Long stratTime = System.nanoTime();  
    for (int i = 0; i < list.size(); i++) {  
          
    }  
    Long  endTime = System.nanoTime();  
    System.out.println("未优化list耗时:"+(endTime - stratTime));  
    

    优化后代码示例

    Long  stratTime = System.nanoTime();  
    int size = list.size();  
    for (int i = 0; i < size; i++) {  
          
    }  
    Long  endTime = System.nanoTime();  
    System.out.println("优化list耗时:"+(endTime - stratTime));  
    

    运行结果

    未优化list耗时:27375  
    优化list耗时:2444  
    
    2.2原理
    list.size()每次循环都会被执行一次,这无疑会影响程序的性能,所以应该将其放到循环外面,用一个变量来代替,优化前后的对比也很明显。
    

    3. 异常捕获

    3.1 代码示例

    优化前代码示例

    Long stratTime = System.nanoTime();  
    for (int i = 0; i < 10000000; i++) {  
        try {  
        } catch (Exception e) {  
        }  
    }  
    Long  endTime = System.nanoTime();  
    System.out.println("在内部捕获异常耗时:"+(endTime - stratTime));  
    

    优化后代码示例

    Long  stratTime = System.nanoTime();  
    try {  
        for (int i = 0; i < 10000000; i++) {  
        }  
    } catch (Exception e) {  
      
    }  
    Long  endTime = System.nanoTime();  
    System.out.println("在外部捕获异常耗时:"+(endTime - stratTime));  
    

    运行结果

    在内部捕获异常耗时:12150142  
    在外部捕获异常耗时:1955  
    
    3.2 总结

    捕获异常是很耗资源的,所以不要讲try catch放到循环内部,优化后同样有好几个数量级的提升。

    结尾

    性能优化的内容有很多,代码优化只是其中一小部分,我们在日常开发中应养成良好的编码习惯。接下来会跟大家探讨更多关于性能优化的内容,希望大家积极交流指导。

    关注我们

    如果需要源码可以关注“IT实战联盟”公号并留言(源码名称+邮箱),小萌看到后会联系作者发送到邮箱,也可以加入交流群和作者互撩哦~~~

    IT实战联盟博客:http://blog.100boot.cn

    展开全文
  • 理解 Python 的 for 循环

    千次阅读 2020-12-08 17:12:02
    在本篇博客中,我们将讨论 Python 中 for 循环原理。我们将从一组基本例子和它的语法开始,还将讨论与 for 循环关联的 else 代码块的用处。然后我们将介绍迭代对象、迭代器和迭代器协议,还会学习如何创建自己的...
  • vue自定义指令原理

    千次阅读 2020-05-05 20:03:07
    vue指令本质 指令本质上是装饰器,是vue对HTML元素的扩展,给HTML元素增加自定义功能,语义化HTML标签。vue编译DOM时,会执行与指令关联的JS代码,即找到指令对象,执行指令对象的相关方法。 自定义指令生命周期 ...
  • for-each循环是jdk1.5引入的新的语法功能。并不是所有东西都可以使用这个循环的。可以看下Iterable接口的注释,它说明了除了数组外,其他类想要使用for-each循环必须实现这个接口。这一点表明除了数组外的for-each...
  • kuka机器人循环指令码垛编程探索

    千次阅读 2020-12-22 12:17:03
    kuka机器人循环指令码垛编程探索张宝强【摘要】【摘要】kuka机器人已经广泛地应用到了各行各业中,主要应用与焊接、装配、搬运和码垛等。Kuka机器人有专用的码垛工艺包,本文讲解用一般的指令及循环指令编写6轴kuka...
  • 第三部分:数组与for循环上一篇文章当中我们简单介绍了宏指令的选择语句:if…else的应用。可以看出,宏指令当中的选择语句同C语言或者VB语言当中的选择语句是类似的用法。本节我们将介绍另外一种重要的语句结构:...
  • 计算机科学与技术专业指令系统设计大作业,cop2000实验软件。设计指令系统并实现了乘法,快速幂,冒泡排序。
  • assign (赋值):作用于工作内存的变量,它把一个从执行引擎接收到的值赋给工作内存的变量 store (存储):作用于工作内存的变量,把工作内存中的一个变量的值传送到主内存中,以便随后的write的操作 write (写入):...
  • 每个人的生活和工作都充满了循环,很多时候,循环意味着重复和枯燥。比如你要手动输入200个员工的个人信息,比如她要一遍一遍地给不认真的人费劲口舌解释同样的事情,又比如他一成不变、如同一潭死水般的生活。...
  • 计算机组成原理(4.3)—— MIPS指令系统(RSIC)

    千次阅读 多人点赞 2020-08-16 22:03:24
    前文链接:计算机组成原理(4.1)—— 指令系统设计 文章目录一、MIPS架构基础1. 寄存器数据指定(1)MIPS架构中的寄存器安排(2)寄存器名称、编号和功能2. 存储器数据指定二、操作数类型和表示方式1. 操作数类型...
  • Vue的v-for遍历中的自定义指令无效

    千次阅读 2020-06-20 22:47:45
    在v-for循环中,通过自定义指令获取循环中每一项的值,然后进行处理,第一次进行v-for遍历,可以正常获取值,并渲染页面,但是list被重新赋值,第二次v-for遍历的时候,在页面中可以直接获取值,但是在指令中无法...
  • 计算机指令与运算基础原理笔记

    千次阅读 2019-10-02 16:59:48
    一、汇编代码与指令 1.要让程序在一个 Linux 操作系统上跑起来,需要把整个程序翻译成汇编语言(ASM,Assembly Language)的程序,这个过程叫编译(Compile)成汇编代码。针对汇编代码,可以再用汇编器(Assembler...
  • LOOP指令

    万次阅读 2021-01-28 20:21:33
    3、通常用loop指令来实现循环功能,cx中存放循环次数 4、大于9FFFH的十六进制数据A000H、A001H...FFFEH、FFFH等,在书写的时候都是以字母开头的。 而在汇编程序中数据不能以字母开头,所以要在前面加0 ...
  • 形状如下:***************要求:完全使用C++的信息输出方式,即cout以及流插入操作符编写,不得使用c语言中的printf等函数使用循环语句进行星号的输出控制,可以使用for、while、do-while任何一种。代码如下:#...
  • 1.并行与指令:同步。当不同的任务之间需要访问问一个位置的数据的时候,就会出现数据竞争的风险,这个时候急需要同步来处理,负责就会引起程序运行错误的结果。同步运行需要依赖于硬件提供的同步指令,可以由用户...
  • 指令寻址方式是指指令或者操作数有效地址的寻找方式,主要分为数据寻址和指令寻址。指令的地址码字段往往并不是操作数的真实地址,而是形式地址,用A表示,(A)即操作数形式地址所指向的存储介质的数值。用形式地址...
  • 指令系统4.1 指令系统的基本概念4.1.1 指令的基本格式1) 四地址指令2) 三地址指令3) 二地址指令4) 一地址指令5) 零地址指令4.1.2 定长操作码指令格式4.1.3 扩展操作码指令格式4.1.4 多字长指令格式4.1.5 指令格式的...
  • Vue v-for循环数据key作用

    千次阅读 2020-07-02 23:15:55
    而隐藏在背后的原理便是其高效的Diff算法。 要使用key来给每个节点做一个唯一标识,Diff算法就可以正确的识别此节点,找到正确的位置区插入新的节点。 key的作用主要是为了高效的更新虚拟DOM。另外vue中在使用相同...
  • java增强型for循环和普通循环比较

    千次阅读 2018-07-01 11:34:43
    增强型for循环和普通循环比较: ... (1)对于非集合类(没有实现 Iterable接口)的数组遍历,增强型for循环和普通循环遍历原理相同,效率相同 &nbsp;&nbsp; &nbsp; (2)对于集合类(实现了Iterable接口),...
  • OpenMP的#pragma omp parallel for指令理解

    千次阅读 2020-11-29 16:54:00
    下面是一个应用#pragma omp parallel for的一串代码 #include<stdio.h> #include<omp.h> int main(){ #pragma omp parallel for for (int i = 0; i < 20; i++) { printf("this is No.%d ...
  • 文章目录Mips复习程序执行的基本原理指令格式及操作数寄存器$0:0号寄存器,其值恒为0s0 s0~s0 s7:程序员变量t0 t0~t0 t9:临时变量立即数主存单元指令集与汇编程序主存变量声明读存储器lb & ...
  • 目录 1.Nginx介绍 1.1 什么是Nginx? 1.2Nginx能做什么 1.3 为什么要选择用Nginx 2.Nginx的安装与配置 2.1 Nginx 安装 ...3.Nginx工作原理 3.1 工作原理: 3.1.1 Nginx处理Request请求过程解析 ...
  • 自己在图上画出来指令的执行过程就会很清晰明了。首先确认有哪些模块,PC,指令存储器,CU,ACC,ALU,数据存储器,像多路选择器,符号位扩展模块都是根据指令后来添加的。再确认模块的输入信号和输出信号。当然,我也...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 74,786
精华内容 29,914
关键字:

循环指令for的工作原理